MySql - 自连接 - 完整 Table 扫描(无法扫描索引)

MySql - Self Join - Full Table Scan (Cannot Scan Index)

我有以下自连接查询:

SELECT A.id
FROM mytbl      AS A
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt)

查询速度很慢,查看执行计划后,原因似乎是 JOIN 中的完整 table 扫描。 table 只有 500 行,我怀疑这是问题所在,我将其增加到 100,000 行,以查看它是否对优化器的选择产生影响。 它没有,有 10 万行它仍在进行完整的 table 扫描。

我的下一步是尝试使用以下查询强制建立索引,但同样的情况出现了,完整的 table 扫描:

SELECT A.id
FROM categories_nested_set      AS A
LEFT JOIN categories_nested_set AS B 
FORCE INDEX (idx_lft, idx_rgt)
ON (A.lft BETWEEN B.lft AND B.rgt)

所有列(id、lft、rgt)都是整数,都已编入索引。

为什么 MySql 在这里进行全面 table 扫描?

如何更改我的查询以使用索引而不是完整的 table 扫描?

CREATE TABLE mytbl ( lft int(11) NOT NULL DEFAULT '0', 
 rgt int(11) DEFAULT NULL, 
 id int(11) DEFAULT NULL,
 category varchar(128) DEFAULT NULL,
  PRIMARY KEY (lft), 
  UNIQUE KEY id (id), 
  UNIQUE KEY rgt (rgt), 
  KEY idx_lft (lft), 
  KEY idx_rgt (rgt) ) ENGINE=InnoDB DEFAULT CHARSET=utf8

谢谢

你有很多索引,其中一些是多余的。让我们从清理其中的一些开始。过多的索引会减慢插入和更新的速度。

PRIMARY KEY (lft),
KEY idx_lft (lft), 

既然你已经在 lft 上定义了一个主键,那么就没有必要再在 lft 上创建另一个索引了。与 rgt 上的唯一索引类似,不需要下面列出的第二个索引。

UNIQUE KEY rgt (rgt), 
KEY idx_rgt (rgt)

现在让我们看看您的查询。

SELECT A.id
FROM mytbl      AS A
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt)

这不太可能是在野外遇到的查询。有 500 行,这个查询甚至可能产生 5000 行?您真的需要一次创建整个密钥吗?这个查询慢的原因是因为 mysql 只能 optimize range comparisions 常量。您的实际查询更有可能如下所示:

SELECT B.*
FROM mytbl      AS A
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 
WHERE a.id = N;

为特定 ID 创建节点的位置。这将使用索引并且会非常快。优化您根本不会使用太多的查询有什么意义?

以下 SO 问题对解决方案至关重要,因为关于邻接列表和索引组合的信息很少:

MySQL & nested set: slow JOIN (not using index)

添加基本比较条件似乎会触发索引的使用,如下所示:

SELECT A.id
FROM mytbl      AS A
LEFT JOIN mytbl AS B ON (A.lft BETWEEN B.lft AND B.rgt)
-- THE FOLLOWING DUMMY CONDITIONS TRIGGER INDEX
WHERE A.lft > 0
AND B.lft > 0
AND B.rgt > 0

不再 table 次扫描。

编辑:查询的固定版本和非固定版本之间的 EXPLAIN 函数比较: