我有一个应用程序需要更新层次结构中的节点,从 ID 已知的特定节点向上。我使用以下 MySQL 语句来执行此操作:
update node as A
join node as B
on A.lft<=B.lft and A.rgt>=B.rgt
set A.count=A.count+1 where B.id=?
该表在 id 上有一个主键,在 lft 和 rgt 上有索引。该语句有效,但我发现它存在性能问题。查看相应select语句的EXPLAIN结果,发现“B”表检查的行数非常多(可能是整张表)。
我可以轻松地将查询分成两个单独的查询:
select lft, rgt from node where id=?
LFT=result.lft
RGT=result.rgt
update node set count=count+1 where lft<=LFT and rgt>=RGT
但为什么原始语句没有按预期执行,我需要如何重新表述才能更好地工作?
应要求,这里是创建表的简化版本:
CREATE TABLE `node` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`lft` decimal(64,0) NOT NULL,
`rgt` decimal(64,0) NOT NULL,
`count` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `name` (`name`),
KEY `location` (`location`(255)),
KEY `lft` (`lft`),
KEY `rgt` (`rgt`),
) ENGINE=InnoDB
我没有尝试添加复合索引(实际上,我没有现场执行此操作所需的访问级别);但我看不出它有什么帮助,试图思考数据库引擎将如何尝试解决双重不平等。
最佳答案
您可以“强制”(至少到 5.5,5.6 版对优化器进行了多项改进,这可能会使重写变得多余)MySQL 通过将拆分的第一部分作为子查询,然后将其用作派生表并连接到表 A:
UPDATE node AS a
JOIN
( SELECT lft, rgt
FROM node
WHERE id = ?
) AS b
ON a.lft <= b.lft
AND a.rgt >= b.rgt
SET
a.count = a.count + 1 ;
效率仍然取决于选择两个索引中的哪一个来限制要更新的行。在使用这两个索引中的任何一个之后,仍然需要进行表查找来检查另一列。所以,我建议你在 (lft, rgt)
上添加一个复合索引,在 (rgt, lft)
上添加一个复合索引,这样只有一个索引用于查找应更新的行.
我假设您正在使用 Nested Set 并且此更新的效率在大表上不会很好,因为查询有 2 个范围条件并且限制了 B 树索引的效率。
关于mysql - 为什么这个 update-with-join mysql 查询这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18871200/