MySql - 自加入 - 全表扫描(无法扫描索引)

时间:2016-07-21 04:38:57

标签: mysql indexing self-join mptt full-table-scan

我有以下自加入查询:

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)

Execution plan for full table scan query :/

所有列(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

由于

2 个答案:

答案 0 :(得分: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只能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创建节点的位置。这将使用索引,并将非常快。如果有的话,优化查询的重点是什么?

答案 1 :(得分:-1)

以下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

不再有桌面扫描。

编辑:查询的固定版本和不固定版本之间的EXPLAIN函数比较: EXPLAIN function results, top is fixed, bottom is not

相关问题