MySQL:使用三个连接优化查询

MySQL:使用三个连接优化查询,mysql,database-design,Mysql,Database Design,第一件事:我正在做的工作非常好。我只是想看看是否有改进的余地,我做事情的方式是否符合标准和/或使用良好的实践 这些是有问题的表格: 项目 主题 item\u主题 类似审计的项目 这是我的用例: 有主题,可以包含许多项 每个项目上可以有N个喜欢的项目 对于每个like,一条记录存储在item\u like\u audit表中,以便以后可以查询该记录以进行排名 这就是查询试图实现的目标: 获取过去7天内最受欢迎的某个主题下的所有项目 以下查询或基础架构是否可以以任何方式进行改进(以提高

第一件事:我正在做的工作非常好。我只是想看看是否有改进的余地,我做事情的方式是否符合标准和/或使用良好的实践

这些是有问题的表格:

  • 项目
  • 主题
  • item\u主题
  • 类似审计的项目
这是我的用例:

  • 主题
    ,可以包含许多
  • 每个
    项目
    上可以有N个喜欢的项目
  • 对于每个like,一条记录存储在
    item\u like\u audit
    表中,以便以后可以查询该记录以进行排名
这就是查询试图实现的目标:

  • 获取过去7天内最受欢迎的某个主题下的所有项目

以下查询或基础架构是否可以以任何方式进行改进(以提高性能或内存)

查询:

SELECT DISTINCT item.* FROM item

/* Match items under this specific topic */
JOIN topic
    ON topic.slug = ?
    AND topic.deleted_at IS NULL
JOIN item_topic
    ON item_topic.item_id = item.id
    AND item_topic.topic_id = topic.id
    AND item_topic.deleted_at IS NULL

/* Match items that have had "like" activity in the past 7 days */
JOIN item_like_audit
    ON item_like_audit.item_id = item.id
    AND item_like_audit.created_at <= (CURRENT_DATE + INTERVAL 7 DAY)
WHERE item.deleted_at IS NULL

/* Order by highest like count to lowest */
ORDER BY item.like_count DESC

/* Pagination */
LIMIT ? OFFSET ?
CREATE TABLE item (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,

    name VARCHAR(255) NOT NULL,
    slug VARCHAR(255) NOT NULL UNIQUE,
    tagline VARCHAR(255) NOT NULL,
    description VARCHAR(1000) NOT NULL,
    price FLOAT NOT NULL,
    like_count INT(10) NOT NULL DEFAULT 0,
    images VARCHAR(1000) NOT NULL,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id)
);

CREATE TABLE item_like_audit (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,

    item_id INT(10) UNSIGNED NOT NULL,
    user_id INT(10) UNSIGNED NOT NULL,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,

    PRIMARY KEY (id),
    KEY `item_like_audit_created_at_index` (`created_at`)
);

CREATE TABLE topic (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,

    name VARCHAR(255) NOT NULL,
    slug VARCHAR(255) NOT NULL UNIQUE,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id)
);

CREATE TABLE item_topic (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,

    item_id INT(10) NOT NULL,
    topic_id INT(10) NOT NULL,

    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id)
);

由于您只返回项目记录,因此可以尝试以下方法以提高性能:

select Item.* 
  from Item
 where Item.deleted_at is null
   and exists (select 1 from item_topic
                where item_topic.item_id = item.id
                  and itme_topic.deleted_at is null
                  and exists (select 1 from topic
                               where topic.id = item_topic.item_id
                                 and topic.deleted_at is null
                                 and topic.slug = ?))
   and exists (select 1 from item_like_audit
                where item_like_audit.item_id = item.id
                  and item_liek_audit.created_at >= (current_date - interval 7 day))
 order by Item.like_count desc
这可能会提高性能,因为:

  • 您不需要
    DISTINCT
    运算符
  • 数据库只需从每个支持表中找到与约束匹配的一行,而不是所有匹配的记录

由于您只返回项目记录,您可以尝试以下方法以提高性能:

select Item.* 
  from Item
 where Item.deleted_at is null
   and exists (select 1 from item_topic
                where item_topic.item_id = item.id
                  and itme_topic.deleted_at is null
                  and exists (select 1 from topic
                               where topic.id = item_topic.item_id
                                 and topic.deleted_at is null
                                 and topic.slug = ?))
   and exists (select 1 from item_like_audit
                where item_like_audit.item_id = item.id
                  and item_liek_audit.created_at >= (current_date - interval 7 day))
 order by Item.like_count desc
这可能会提高性能,因为:

  • 您不需要
    DISTINCT
    运算符
  • 数据库只需从每个支持表中找到与约束匹配的一行,而不是所有匹配的记录
假设
item\u-topic(item\u-id,topic\u-id)
是唯一的,我们可以通过去掉
DISTINCT
关键字,并将
item\u-like\u-audit
的检查重写为一个存在相关子查询,而不是一个联接操作,从而取消“使用文件排序”操作

如果我们有一个独特性的保证

  CREATE UNIQUE INDEX item_topic_UX1 ON item_topic (topic_id, item_id);
我们已经保证了
主题(slug)
主题(id)
项目(id)

  SELECT item.* 
    FROM item

/* Match items under this specific topic */
    JOIN item_topic
      ON item_topic.item_id = item.id
     AND item_topic.deleted_at IS NULL
    JOIN topic
      ON topic.id    = item_topic.topic_id
     AND topic.slug  = ?
     AND topic.deleted_at IS NULL

   WHERE item.deleted_at IS NULL
/* Match items that have had "like" activity in the past 7 days */
     AND EXISTS ( SELECT 1
                    FROM item_like_audit
                   WHERE item_like_audit.item_id = item.id
                     AND item_like_audit.created_at >= DATE(NOW()) + INTERVAL -7 DAY
                 )

/* Order by highest like count to lowest */
  ORDER BY item.like_count DESC
为了提高相关子查询的性能,我们可以创建一个覆盖索引

我们希望前面创建的唯一索引将用于连接操作,这样也可以提高性能。如果在列中包含
deleted\u,我们可以得到一个覆盖索引

  CREATE INDEX item_topic_IX2 ON item_topic (topic_id, item_id, deleted_at)
这与我们之前创建的唯一索引是多余的,如果我们仍然想保证唯一性,请翻转列的顺序

  DROP INDEX item_topic_UX1 ON item_topic ;
  CREATE UNIQUE INDEX item_topic_UX1 ON item_topic (item_id,topic_id);
如果我们不能保证唯一性,那么我倾向于在
DISTINCT
关键字上添加
groupbyitem.id
子句


使用
EXPLAIN
查看执行计划,并验证是否使用了适当的索引


如果我们不能保证
项目主题
(项目id,主题id)
的唯一性,并且
分组依据
操作的“使用文件排序”操作的开销仍然过高

我们可以尝试使用EXISTS检查“匹配主题”条件。(但我不太希望这会更快。)

我们需要为相关子查询的性能提供合适的索引。

假设
item\u-topic(item\u-id,topic\u-id)
是唯一的,我们可以通过去掉
DISTINCT
关键字来取消“使用文件排序”操作,以及将
项的检查重写为现有的相关子查询,而不是联接操作

如果我们有一个独特性的保证

  CREATE UNIQUE INDEX item_topic_UX1 ON item_topic (topic_id, item_id);
我们已经保证了
主题(slug)
主题(id)
项目(id)

  SELECT item.* 
    FROM item

/* Match items under this specific topic */
    JOIN item_topic
      ON item_topic.item_id = item.id
     AND item_topic.deleted_at IS NULL
    JOIN topic
      ON topic.id    = item_topic.topic_id
     AND topic.slug  = ?
     AND topic.deleted_at IS NULL

   WHERE item.deleted_at IS NULL
/* Match items that have had "like" activity in the past 7 days */
     AND EXISTS ( SELECT 1
                    FROM item_like_audit
                   WHERE item_like_audit.item_id = item.id
                     AND item_like_audit.created_at >= DATE(NOW()) + INTERVAL -7 DAY
                 )

/* Order by highest like count to lowest */
  ORDER BY item.like_count DESC
为了提高相关子查询的性能,我们可以创建一个覆盖索引

我们希望前面创建的唯一索引将用于连接操作,这样也可以提高性能。如果在
列中包含
deleted\u,我们可以得到一个覆盖索引

  CREATE INDEX item_topic_IX2 ON item_topic (topic_id, item_id, deleted_at)
这与我们之前创建的唯一索引是多余的,如果我们仍然想保证唯一性,请翻转列的顺序

  DROP INDEX item_topic_UX1 ON item_topic ;
  CREATE UNIQUE INDEX item_topic_UX1 ON item_topic (item_id,topic_id);
如果我们不能保证唯一性,那么我倾向于在
DISTINCT
关键字上添加
groupbyitem.id
子句


使用
EXPLAIN
查看执行计划,并验证是否使用了适当的索引


如果我们不能保证
项目主题
(项目id,主题id)
的唯一性,并且
分组依据
操作的“使用文件排序”操作的开销仍然过高

我们可以尝试使用EXISTS检查“匹配主题”条件。(但我不太希望这会更快。)


我们需要为相关子查询的性能提供合适的索引。

注意:
created_at=DATE(NOW())+INTERVAL-7天
@spencer7593太棒了!谢谢你。由于种子数据中的所有内容的
created_at
字段设置为今天,我得到了一个类似的结果集。注意:
created_at=DATE(NOW())+INTERVAL-7天
@spencer7593太棒了!谢谢你。我得到了一个类似的结果集,因为对于种子数据中的所有内容,
created\u at
字段设置为今天。在这种情况下,我实际上希望所有结果都基于
限制和
偏移量。因此,如果我有
限制10
,我想要10个结果,而不仅仅是一个。结果集本质上是一个“排名靠前的列表”,就其在UI中的显示方式而言