2

次のツリー状のテーブル構造を考えてみましょう。

CREATE TABLE nodes(
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  parent INTEGER,
  descendant_count INTEGER NOT NULL DEFAULT 0,
  FOREIGN KEY(parent) REFERENCES nodes(id) ON DELETE CASCADE
);

descendant_count列には、子孫レコードの数が格納されます。

現在、新しい挿入ごとに値を増やす (または削除するたびに値を減らす) ことにより、手動で維持しています。基本的に、親レコードを取得し続けてから実行します

 UPDATE nodes SET descendant_count = (descendant_count + 1) ? WHERE...

ルートに到達するまで、各親で。明らかに、これは深くネストされた構造では非常に遅くなります。

これを達成するためにトリガーを使用することは可能ですか? または、それを行うためのより高速で信頼性の高い方法はありますか?


アップデート - 11.08.03

SQLite は再帰トリガーをサポートしているようです。したがって、1 つのノードのみのカウントを更新すると、トリガーはすべての親ノードのカウントを更新できるはずです。

CREATE TRIGGER setCounts AFTER UPDATE ON nodes
WHEN (NEW.descendant_count <> OLD.descendant_count)
BEGIN

  -- subtract old counts
  UPDATE nodes
    SET descendant_count = descendant_count - OLD.descendant_count
    WHERE id = NEW.parent;

  -- add new counts
  UPDATE nodes
    SET descendant_count = descendant_count + NEW.descendant_count
    WHERE id = NEW.parent;
END;

調べてみたところ数値は合っているようで、やっぱりこれってありえるの?

4

4 に答える 4

2

次のようにソリューションを最適化できます。更新はツリーを再帰的にカスケードするため、これはかなりの節約になります...

CREATE TRIGGER setCounts AFTER UPDATE ON nodes
WHEN (NEW.descendant_count <> OLD.descendant_count)
BEGIN
  IF NEW.parent_id IS NOT NULL THEN
      UPDATE nodes
      SET descendant_count = descendant_count 
          + NEW.descendant_count - OLD.descendant_count
      WHERE id = NEW.parent;
  END IF;
END;

さらに、親を再割り当てする場合を処理する必要があります。例えば:

update node set parent_id = 20 WHERE parent_id = 10

そのためには別のトリガーが必要です

CREATE TRIGGER setCounts2 AFTER UPDATE ON nodes
WHEN (NEW.parent_id <> OLD.parent_id)
BEGIN
  IF OLD.parent_id IS NOT NULL THEN
      UPDATE nodes SET descendant_count = descendant_count - OLD.descendant_count
      WHERE id = OLD.parent;
  END IF;

  IF NEW.parent_id IS NOT NULL THEN
      UPDATE nodes SET descendant_count = descendant_count + NEW.descendant_count
      WHERE id = NEW.parent;
  END IF;
END;
于 2013-11-12T17:44:14.760 に答える