挑戦する
タスクは前の
記事と同じで、MySQLにのみ適用できます。
すくい
良いニュースだ!
MySQLの再帰トリガーに問題はありません!
MySQL開発者は、トリガーレベルでも変更可能なテーブルを愚かにロックするだけです。これが大根です。 しかし、実際には、停電だけが私たちを止めることができます。
小さな抜け穴があり、結合されたテーブルがあります。 私はそれが非常に特別に考案されたことを文書で確認しませんでしたが、否定もありませんでした。 確かに、この抜け穴はカバーされる可能性がありますが、ポイントはわかりません。
悲しいかな、
MySQLのトリガーメカニズム
は新しく、かなり粗雑なものであり、その使用にいくつかの制限を課していますが、それでも問題を解決するには十分です。
したがって、ソーステーブル:
SQLコード(1)
CREATE TABLE `ns_tree`(
`id` int(11)NOT NULL auto_increment、
`left_key` int(11)NOT NULLデフォルト '0'、
`right_key` int(11)NOT NULLデフォルト '0'、
`level` int(11)NOT NULLデフォルト '0'、
`parent_id` int(11)NOT NULLデフォルト '0'、
`tree` int(11)NOT NULLデフォルト '1'、
`field1`テキスト、
主キー( `id`)
)エンジン= MyISAM;
それに基づいて、まったく同じフィールドセットを持つまったく同じテーブルを作成します。
SQLコード(2)
CREATE TABLE `_ns_tree`(
`id` int(11)NOT NULL auto_increment、
`left_key` int(11)NOT NULLデフォルト '0'、
`right_key` int(11)NOT NULLデフォルト '0'、
`level` int(11)NOT NULLデフォルト '0'、
`parent_id` int(11)NOT NULLデフォルト '0'、
`tree` int(11)NOT NULLデフォルト '1'、
`field1`テキスト、
主キー( `id`)
)エンジン=挿入METHOD =最後の結合=( `ns_tree`);
キーワード
-MERGEは基本的にテーブルのビューを作成し、それをテーブルとして使用できます。 一般に、MERGEメカニズムも少し湿っています。 結合されたテーブルの構造は、元のテーブルとまったく同じでなければなりません。
重要!!! 元のテーブルの構造を変更すると、テーブル間の接続は失われますが、すでに接続されている行の間では-NO! 注意してください。 ソーステーブルの構造を変更するとき、同じ変更を結合されたものに適用する必要があります!元のテーブルもMyISAMである必要があります。これは理解可能であり、この場合のトランザクションは、テーブルが相互にロックできず、テーブル内のデータが同じであるため、適用できません。 ただし、トリガーの制限内でソーステーブルがロックされ、結合されたテーブルのデータをトリガーから変更するため、これは怖いことではありません。
また、私が注意したいこと:テーブルが完全にブロックされ、キューが長くなりすぎる可能性があるため、同じツリー内でルートノードを使用しないことを強くお勧めします(以前の記事で述べたように)。
レコードを作成
MySQLトリガー方言は、
PostgreSQL方言とわずかに異なり
ます 。
- プロシージャの開始時に変数を宣言する必要はありません。
- トリガーの実行を中断することは不可能です。完全に解決する必要があります。
- 上記のように、再帰を恐れていないため、追加のチェックや追加のフィールドは必要ありません。
したがって、ロジックは多少変更されます。
- 変数は途中で表示されます。
- トリガーから戻る代わりに、コードを条件でラップします。
- ツリーの構造に関するすべての変更は、補助ジョイントテーブルに適用されます。
SQLコード(3)
CREATE DEFINER = 'user' @ 'localhost' TRIGGER `ns_tree_before_ins_tr` BEFORE INSERT ON` ns_tree`
各行ごと
開始
SET @left_key:= 0;
SET @level:= 0;
-親を指定した場合:
NEW.parent_idがNULLではなく、NEW.parent_id> 0の場合
SELECT right_key、 `level` + 1 INTO @left_key、@level
ns_treeから
WHERE id = NEW.parent_id AND tree = NEW.tree;
終了IF;
-左キーを指定した場合:
NEW.left_keyがNULLではなく、NEW.left_key> 0かつ
(@left_keyはNULLまたは@left_key = 0)THEN
SELECT id、left_key、right_key、 `level`、parent_id
INTO @ tmp_id、@ tmp_left_key、@ tmp_right_key、@ tmp_level、@ tmp_parent_id
ns_treeから
WHEREツリー= NEW.tree AND(left_key = NEW.left_key OR right_key = NEW.left_key);
IF @tmp_left_keyがNULLではなく、かつ@tmp_left_key> 0 AND NEW.left_key = @tmp_left_key THEN
SET NEW.parent_id:= @tmp_parent_id;
SET @left_key:= NEW.left_key;
SET @level:= @tmp_level;
ELSEIF @tmp_left_keyはNULLではなく、@ tmp_left_key> 0 AND NEW.left_key = @tmp_right_key THEN
SET NEW.parent_id:= @tmp_id;
SET @left_key:= NEW.left_key;
SET @level:= @tmp_level + 1;
終了IF;
終了IF;
-親キーまたは左キーが指定されていない場合、または何も見つからなかった場合
@left_keyがNULLまたは@left_key = 0の場合
SELECT MAX(right_key)+ 1 INTO @left_key
ns_treeから
WHEREツリー= NEW.tree;
@left_keyがNULLまたは@left_key = 0の場合
SET @left_key:= 1;
終了IF;
SET @level:= 0;
SET NEW.parent_id:= 0;
終了IF;
-新しいキー値を設定する
SET NEW.left_key:= @left_key;
SET NEW.right_key:= @left_key + 1;
SET NEW.`level`:= @level;
-ツリーにギャップを形成する
UPDATE _ns_tree
SET left_key = case when when left_key> = @left_key
THEN left_key + 2
ELSE left_key + 0
END、
right_key = right_key + 2
WHEREツリー= NEW.tree AND right_key> = @left_key;
終了
レコードを編集
原則は、方言の使用と同じです。
SQLコード(4)
CREATE DEFINER = 'user' @ 'localhost' TRIGGER `ns_tree_before_upd_tr` BEFORE UPDATE ON` ns_tree`
各行ごと
開始
-フィールドの変更、または不快なものの送信を禁止します
SET NEW.tree:= OLD.tree;
SET NEW.right_key:= OLD.right_key;
SET NEW.`level`:= OLD.`level`;
SET @return_flag:= 0;
NEW.parent_idがNULLの場合、NEW.parent_idを設定します:= 0; 終了IF;
-ツリーの構造に関連する変更があるかどうかを確認します
NEW.parent_id <> OLD.parent_idまたはNEW.left_key <> OLD.left_keyの場合
-ツリーを再構築しています。先に進みましょう。
SET @left_key:= 0;
SET @level:= 0;
SET @skew_tree:= OLD.right_key-OLD.left_key + 1;
-移動先を決定します。
-parent_idが変更された場合:
NEW.parent_id <> OLD.parent_idの場合
-別の悪にさらされている場合:
NEW.parent_id> 0の場合
SELECT right_key、レベル+ 1
INTO @ left_key、@ level
ns_treeから
WHERE id = NEW.parent_id AND tree = NEW.tree;
-それ以外の場合、ツリーのルートに転送します。
その他
SELECT MAX(right_key)+ 1
INTO @left_key
ns_treeから
WHEREツリー= NEW.tree;
SET @level:= 0;
終了IF;
-突然親が移動したノードの範囲内にある場合は、以下を確認してください。
@left_keyがNULLではない場合
@left_key> 0 AND
@left_key> OLD.left_key AND
@left_key <= OLD.right_key THEN
SET NEW.parent_id:= OLD.parent_id;
SET NEW.left_key:= OLD.left_key;
SET @return_flag:= 1;
終了IF;
終了IF;
-parent_idではない場合、left_keyが変更されるか、parent_idを変更しても何も生じない場合
@left_keyがNULLまたは@left_key = 0の場合
SELECT id、left_key、right_key、 `level`、parent_id
INTO @ tmp_id、@ tmp_left_key、@ tmp_right_key、@ tmp_level、@ tmp_parent_id
ns_treeから
WHEREツリー= NEW.tree AND(right_key = NEW.left_key OR right_key = NEW.left_key-1)
LIMIT 1;
@tmp_left_keyがNULLではない場合
@tmp_left_key> 0 AND
NEW.left_key-1 = @tmp_right_key THEN
SET NEW.parent_id:= @tmp_parent_id;
SET @left_key:= NEW.left_key;
SET @level:= @tmp_level;
ELSEIF @tmp_left_keyはNULLではない
@tmp_left_key> 0 AND
NEW.left_key = @tmp_right_key THEN
SET NEW.parent_id:= @tmp_id;
SET @left_key:= NEW.left_key;
SET @level:= @tmp_level + 1;
ELSEIF NEW.left_key = 1 THEN
SET NEW.parent_id:= 0;
SET @left_key:= NEW.left_key;
SET @level:= 0;
その他
SET NEW.parent_id:= OLD.parent_id;
SET NEW.left_key:= OLD.left_key;
SET @return_flag = 1;
終了IF;
終了IF;
-これで、ツリーの移動先がわかりました
-やりがいがあるかどうかを確認します
@return_flagがNULLまたは@return_flag = 0の場合
SET @skew_level:= @level-OLD.`level`;
IF @left_key> OLD.left_key THEN
-ツリーを上に移動する
SET @skew_edit:= @left_key-OLD.left_key-@skew_tree;
UPDATE _ns_tree
SET left_key = right_key <= OLD.right_keyの場合
THEN left_key + @skew_edit
その他の場合left_key> OLD.right_keyの場合
THEN left_key-@skew_tree
ELSE left_key
終了
END、
`level` = right_key <= OLD.right_keyの場合
THEN `レベル` + @skew_level
ELSE `レベル`
END、
right_key = right_key <= OLD.right_keyの場合
THEN right_key + @skew_edit
その他のケースright_key <@left_keyの場合
THEN right_key-@skew_tree
ELSE right_key
終了
終了
WHEREツリー= OLD.tree AND
right_key> OLD.left_key AND
left_key <@left_key AND
id <> OLD.id;
SET @left_key:= @left_key-@skew_tree;
その他
-ツリーを下に移動する:
SET @skew_edit:= @left_key-OLD.left_key;
UPDATE _ns_tree
セット
right_key = left_key> = OLD.left_keyの場合
THEN right_key + @skew_edit
その他の場合right_key <OLD.left_keyの場合
THEN right_key + @skew_tree
ELSE right_key
終了
END、
`level` = left_key> = OLD.left_keyの場合
THEN `レベル` + @skew_level
ELSE `レベル`
END、
left_key =ケースleft_key> = OLD.left_keyの場合
THEN left_key + @skew_edit
その他の場合left_key> = @left_key
THEN left_key + @skew_tree
ELSE left_key
終了
終了
WHEREツリー= OLD.tree AND
right_key> = @left_key AND
left_key <OLD.right_key AND
id <> OLD.id;
終了IF;
-ツリーが再構築され、現在のノードのみが残りました
SET NEW.left_key:= @left_key;
SET NEW.`level`:= @level;
SET NEW.right_key:= @left_key + @skew_tree-1;
終了IF;
終了IF;
終了
注意!!! MySQLでは、同じクエリのクエリ中に変更されたフィールドが値を新しいものに変更するため、
UPDATEクエリにフィールドがリストされる順序が重要です。条件の下でクエリでこれらのフィールドをさらに使用すると、結果が不十分になります。
レコードを削除
トリガーの再帰には問題がないため、一般に削除は簡単な作業になります。
オプションのトリガー:「ブランチ全体を削除」:
SQLコード(5)
CREATE DEFINER = 'user' @ 'localhost' TRIGGER `ns_tree_before_del_tr` AFTER DELETE ON` ns_tree`
各行ごと
開始
-子ノードを削除します。
_ns_treeから削除
どこ
ツリー= OLD.tree AND
left_key> OLD.left_key AND
right_key <OLD.right_key;
-キーのギャップを削除します。
SET @skew_tree:= OLD.right_key-OLD.left_key + 1;
UPDATE _ns_tree
SET left_key = left_key> OLD.left_keyの場合
THEN left_key-@skew_tree
ELSE left_key
END、
right_key = right_key-@skew_tree
WHERE right_key> OLD.right_key AND
ツリー= OLD.tree AND
id <> OLD.id;
終了
オプションのトリガー:「子ノードを上に移動してノードを削除する」:
SQLコード(6)
CREATE DEFINER = 'user' @ 'localhost' TRIGGER `ns_tree_before_del_tr` AFTER DELETE ON` ns_tree`
各行ごと
開始
-キーのギャップを削除します。
UPDATE _ns_tree
SET left_key = left_key <OLD.left_keyの場合
THEN left_key
その他のケース(right_key <OLD.right_keyの場合)
THEN left_key-1
ELSE left_key-2
終了
END、
parent_id = right_key <OLD.right_key AND `level` = OLD.level + 1の場合
OLD.parent_id
ELSE parent_id
END、
`level` = right_key <OLD.right_keyの場合
THEN `レベル`-1
ELSE `レベル`
END、
right_key = right_key <OLD.right_keyの場合
THEN right_key-1
ELSE right_key-2
終了
WHERE(right_key> OLD.right_keyまたは
(left_key> OLD.left_key AND right_key <OLD.right_key))AND
ツリー= OLD.tree;
終了
ツリーの構造に影響を与える変更はバッチで行う必要はなく、各ノードで順番に行う必要があるため、その整合性が維持されることに注意してください。
実際にはすべて。 インデックスを置くことだけが残っています(ここでも、SQLコマンドを書くのが面倒なので、声を出して説明します)。
- コンポジットはフィールド( left_key、right_key、level、tree )に固有ではありません。
- フィールド( parent_id )で一意ではありません。
お楽しみください;-)
セルゲイ・トムレヴィッチ(別名フェニックス)(07/08/2009)