-1

我有以下自联接查询:

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

查询很慢,在查看执行计划后,原因似乎是 JOIN 中的全表扫描。该表只有 500 行,并且怀疑这是问题,我将其增加到 100,000 行,以查看它是否对优化器的选择产生了影响。它没有,有 100k 行它仍在进行全表扫描。

我的下一步是尝试使用以下查询强制索引,但出现了同样的情况,即全表扫描:

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在这里做全表扫描?

如何更改我的查询以使用索引而不是全表扫描?

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

谢谢

4

2 回答 2

2

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

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只能优化常量的范围比较。您的实际查询更有可能看起来像这样:

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 创建节点的位置。这将使用索引并且会非常快。优化一个您根本不会使用的查询有什么意义?

于 2016-07-21T06:30:30.207 回答
-1

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

MySQL & 嵌套集:慢 JOIN(不使用索引)

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

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

并且没有更多的表扫描。

编辑:查询的固定版本和非固定版本之间的 EXPLAIN 函数比较: EXPLAIN 函数结果,top 是固定的,bottom 不是

于 2016-07-22T00:45:28.743 回答