我有一个应用程序需要更新一个层次结构中的节点,从一个已知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表的缩写版本:
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首先评估表b上的条件,方法是将拆分的第一部分作为子查询,然后将其用作派生表并连接到表。答:

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)上添加一个复合索引,这样就只使用一个索引来查找哪些行应该更新。
我假设您使用的是嵌套集,并且此更新在大表上的效率不会很高,因为查询有两个范围条件,这限制了b树索引的效率。

10-01 23:25
查看更多