7

テーブルで発生するすべての変更を追跡するトリガーを作成しています。残念ながら、テーブルには150以上の列があり、コードに各列を書くのを避けたかったので(例:new.col1、new.col2 ....)、「更新後トリガー」に次のクエリを書きました

INSERT INTO logs SELECT *, NOW() FROM abc WHERE abc.id = NEW.Id;

この考えは、更新クエリで変更されていないデータの重複により、複数の問題を引き起こしています。

一言で言えば、どの列が更新クエリの一部であったかを動的に見つけたいのですが、それが不可能な場合は、「新しい」行のすべての列を反復処理して、old.@colName == new を動的に比較できるようにする方法があります。 .@colName?

Oracle PL/SQL: Loop Over Trigger Columns Dynamicallyt-sql の更新トリガーで何かが変更されたかどうかを判断する方法、およびMySQL UPDATE トリガー: 実際に変更された列の値を INSERTing を見ました 。

最後のリンクは、1 つの違いだけで必要なものに近いものです。同様のトリガーを作成するすべてのテーブルに 100 以上の列があるため、次のステートメントで列名をハードコーディングしたくありません!!

IF NEW.column1 <> OLD.column1 THEN INSERT INTO... END IF; IF NEW.column2 <> OLD.column2 THEN INSERT INTO... END IF
4

2 に答える 2

4

今朝これについて少し調べてみたところ、あなたと同じ検索結果の多くに出くわしたようです. 最終的に、すべてのテーブル列をループして、対応する古い/新しい値を参照する方法がないように思えます。各列を明示的にチェックしてからログに記録することに落ち着きました。

IF (NEW.fld1 <> OLD.fld1) OR (NEW.fld1 IS NOT NULL AND OLD.fld1 IS NULL) OR (NEW.fld1 IS NULL AND OLD.fld1 IS NOT NULL) THEN
 INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`)
 VALUES ("tblname", "fld1", OLD.fld1, NEW.fld1); 
END IF; 

IF (NEW.fld2 <> OLD.fld2) OR (NEW.fld2 IS NOT NULL AND OLD.fld2 IS NULL) OR (NEW.fld2 IS NULL AND OLD.fld2 IS NOT NULL) THEN
 INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`)
 VALUES ("tblname", "fld2", OLD.fld2, NEW.fld2); 
END IF; ...

ここで別の解決策のヒントを見つけました。理論的には、列名用、古い値用、新しい値用の 3 つの区切りリストを持つことができます。古い値と新しい値を明示的に参照する必要がありますが、それは 1 行であり (他のテーブルに実装するために維持またはコピー/貼り付けする方が簡単です)、ループすることができます。したがって、擬似コードでは次のようになります。

fields_array = concat_ws(",", "fld1", "fld2");
old_vals_array = concat_ws(",", OLD.fld1, OLD.fld2);
new_vals_array = concat_ws(",", NEW.fld1, NEW.fld2);

foreach fields_array as key => field_name
     INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`)
     VALUES ("tblname", field_name, old_vals_array[key], vew_vals_array[key]);

私はこれをあまり考えていません。変数を設定するのではなく、ストアド プロシージャを呼び出す必要がある場合があります。でも調べてみる価値はありそうです。私はすでにトリガーに十分な時間を費やしてきました。より洗練されたソリューションで (上司に) 試行錯誤の時間を検証できるかどうかわかりません。

于 2012-08-16T16:38:53.320 に答える
1

ingratiatednerd がすでに提案したように、CONCAT_WS を使用して、必要なすべての値から文字列を作成し、単一の比較ステートメントを作成できます。

おそらく、以下は誰かに役立つでしょう:

DECLARE old_concat, new_concat text;
SET old_concat = CONCAT_WS(',', OLD.fld1, OLD.fld2, ...);
SET new_concat = CONCAT_WS(',', NEW.fld1, NEW.fld2, ...); 

IF old_concat <> new_concat
THEN
   INSERT STATEMENT
END IF;
于 2014-09-19T14:09:11.657 に答える