4

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」テーブルについて検査された行数が非常に多いことがわかりました (おそらくテーブル全体)。

クエリを 2 つの別々のクエリに簡単に引き離すことができます。

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 の省略版を以下に示します。

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

複合インデックスを追加しようとはしていません (実際、その場で追加するために必要なアクセス レベルがありません)。しかし、データベース エンジンが二重の不等式を解決しようとする方法を考えてみると、それがどのように役立つかわかりません。

4

4 に答える 4

8

「強制」することができます (少なくとも 5.5 まで、バージョン 5.6 ではオプティマイザーにいくつかの改善があり、この書き換えが冗長になる可能性があります)。MySQL は、分割の最初の部分をサブクエリとして取得し、次にテーブル B の条件を最初に評価します。これを派生テーブルとして使用し、テーブル 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 ; 

効率は、更新する行を制限するために 2 つのインデックスのどちらを選択するかによって異なります。これら 2 つのインデックスのいずれかを使用した後でも、他の列をチェックするためにテーブル ルックアップが必要です。そのため、複合インデックスを(lft, rgt)1 つ追加して、(rgt, lft)更新する行を見つけるために 1 つのインデックスのみを使用することをお勧めします。

ネストされたセットを使用していると思いますが、クエリに 2 つの範囲条件があり、B ツリー インデックスの効率が制限されるため、この更新の効率は大きなテーブルではあまり高くありません。

于 2013-09-18T11:48:32.823 に答える
3

これは単なる提案です。うまくいくかどうかわかりません。

クエリの問題は、2 つの列に不等式があることです。これにより、両方のインデックスを使用することが非常に難しくなり、join非常に非効率になります。このアイデアは、不等式の各辺に対して 1 つずつ、2 つの結合を行いidon条件に を含めることです。したがって、両方を通過するノードのみが通過します。

UPDATE node a JOIN 
      (SELECT lft, rgt
       FROM node
       WHERE id = ? 
      ) l
      ON a.lft <= l.lft  join
      (SELECT lft, rgt
       FROM node
       WHERE id = ? 
      ) r
      on a.rgt >= r.rgt
    SET a.count = a.count + 1 ; 

おっしゃる通り、うまくいくかどうかはわかりません。explainただし、プランが両方の不等式にインデックスを使用しているかどうかを確認するために、クエリを簡単に確認できるはずです。

于 2013-09-18T12:15:21.823 に答える
1

mysql が更新中のテーブルを参照する際に問題があることは知っていますが、私にとって明らかな解決策は次のとおりです。

update node  A 
set A.count=A.count+1
WHERE EXISTS (
   SELECT *
   FROM node B 
   WHERE B.id=?
   AND A.lft<=B.lft and A.rgt>=B.rgt
   );
于 2013-09-18T12:48:58.587 に答える