当前位置: 首页 > 面试题库 >

如何使JOIN查询使用索引?

卢景澄
2023-03-14
问题内容

我有两个表:

CREATE TABLE `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(1000) DEFAULT NULL,
  `last_updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `last_updated` (`last_updated`),
) ENGINE=InnoDB AUTO_INCREMENT=799681 DEFAULT CHARSET=utf8

CREATE TABLE `article_categories` (
  `article_id` int(11) NOT NULL DEFAULT '0',
  `category_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`article_id`,`category_id`),
  KEY `category_id` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

这是我的查询:

SELECT a.*
FROM
    articles AS a,
    article_categories AS c
WHERE
    a.id = c.article_id
    AND c.category_id = 78
    AND a.comment_cnt > 0
    AND a.deleted = 0
ORDER BY a.last_updated
LIMIT 100, 20

EXPLAIN为此:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: index
possible_keys: PRIMARY
          key: last_updated
      key_len: 9
          ref: NULL
         rows: 2040
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: c
         type: eq_ref
possible_keys: PRIMARY,fandom_id
          key: PRIMARY
      key_len: 8
          ref: db.a.id,const
         rows: 1
        Extra: Using index

last_updated在第一个表上使用的全索引扫描进行排序,但不使用y索引进行连接(type: index在解释中)。这对性能非常不利,并且会杀死整个数据库服务器,因为这是一个非常频繁的查询。

我尝试使用反转表顺序STRAIGHT_JOIN,但这给了filesort, using_temporary,甚至更糟。

有什么办法可以使mysql同时使用索引进行连接和排序?

===更新===

我真的很绝望。也许某种形式的非规范化可以在这里有所帮助?


问题答案:

如果您有很多类别,则无法提高此查询的效率。单个索引不能一次覆盖两个表MySQL

你所要做的非规范化:添加last_updatedhas_commentsdeletedarticle_categories

CREATE TABLE `article_categories` (
  `article_id` int(11) NOT NULL DEFAULT '0',
  `category_id` int(11) NOT NULL DEFAULT '0',
  `last_updated` timestamp NOT NULL,
  `has_comments` boolean NOT NULL,
  `deleted` boolean NOT NULL,
  PRIMARY KEY (`article_id`,`category_id`),
  KEY `category_id` (`category_id`),
  KEY `ix_articlecategories_category_comments_deleted_updated` (category_id, has_comments, deleted, last_updated)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

并运行以下查询:

SELECT  *
FROM    (
        SELECT  article_id
        FROM    article_categories
        WHERE   (category_id, has_comments, deleted) = (78, 1, 0)
        ORDER BY
                last_updated DESC
        LIMIT   100, 20
        ) q
JOIN    articles a
ON      a.id = q.article_id

当然article_categories,每当您更新中的相关列时,您也应该更新article。这可以在触发器中完成。

请注意,该列has_comments是布尔值:这将允许使用相等谓词对索引进行单个范围扫描。

还要注意,LIMIT进入子查询。这将MySQL使用默认情况下不使用的后行查找。请参阅我的博客中有关如何提高性能的文章:

  • MySQL ORDER BY / LIMIT性能:晚行查找

如果您使用的是SQL
Server,则可以在查询上建立可索引的视图,从本质article_categories上讲,它将使用服务器自动维护的带有附加字段的非规范化索引副本。

不幸的是,MySQL它不支持此功能,您将必须手动创建这样的表并编写其他代码以使其与基本表保持同步。



 类似资料:
  • 我可以使用Lucene查询ElasticSearch索引吗? 我使用ElasticSearch创建了一个索引,并插入了以下三个文档: null 不幸的是,d.get(“_source”)也返回null。 如何检索匹配查询的文档字段? 谢谢你。

  • 问题内容: 我开始学习JPA,并已基于我在SQL Server中测试的以下本机SQL实现了JPA查询示例: 通过上面的SQL,我构造了以下JPQL查询: 如您所见,我仍然缺少原始查询中的条件。我的问题是,如何将其放入JPQL? 问题答案: 写这个; 因为您的学生实体与ClassTbl实体具有一对多关系。

  • 问题内容: 基于这两个表(及其对应的实体): 我必须列出所有配置文件,并按其项目的最佳排名进行排序(实际上,这是“顶级配置文件”列表)。 这是您可以在PHPMyAdmin中执行的SQL请求,例如: 我是JPA的新手,我找不到一些使用CriteriaBuilder进行LEFT OUTER JOIN的示例(如果这样做是正确的话)。 如果有人能以正确的方式引导我,我将不胜感激(我不是要求别人做我的工作,

  • 问题内容: 可以请一些人提供如何使用联接编写以下sql查询的方法。我不希望尽量不要使用 in ,我也想替换 where 条件。 我正在使用SQL Server 2008 问题答案: 本文: NOT IN与NOT EXISTS与LEFT JOIN / IS NULL:SQL Server 如果您感兴趣的话。 简而言之,此查询: 可以工作,但是效率不如(或)构造。 您还可以使用以下命令: 这既不使用也

  • 问题内容: 我正在使用SQL Server,但没有足够的数据集来测试查询的性能。 我想分析查询,看看索引是否被利用。我该如何检查 问题答案: 在SQL Management Studio中,只需键入查询,然后按Control-L(显示查询执行计划)。在那里,您将能够查看是否正在使用任何索引。“表扫描”表示未使用索引。“索引扫描”是指使用索引。

  • 我正在使用Spring Boot和JDBCT模板开发一个用于数据库查询的应用程序。 问题是这样的:如果我必须在一个表上询问db,我没有问题。但是,如果我有一个join,我该如何执行这个任务? 更具体地说,创建表的SQL命令如下: 对应的java类如下: 约束是 firewall_Items.firewall_id = firewall.id(因此,这些是我必须用来执行 join 的变量)。 现在,