MS SQL Serverで列の変更を検出する最も効率的な方法 質問する

MS SQL Serverで列の変更を検出する最も効率的な方法 質問する

当社のシステムは SQL Server 2000 で実行されており、SQL Server 2008 へのアップグレードを準備中です。特定の列の変更を検出し、変更があった場合にその列を操作する必要があるトリガー コードが多数あります。

明らかにSQL Serverはアップデート()そして列の更新()関数ですが、これらの関数はSQL文に関係する列のみを示します。ない実際に変更された列。

どの列が変更されたかを判断するには、次のようなコードが必要です (NULL をサポートする列の場合)。

IF UPDATE(Col1)
    SELECT @col1_changed = COUNT(*) 
    FROM Inserted i
        INNER JOIN Deleted d ON i.Table_ID = d.Table_ID
    WHERE ISNULL(i.Col1, '<unique null value>') 
            != ISNULL(i.Col1, '<unique null value>')

このコードは、テスト対象の列ごとに繰り返す必要があります。その後、「変更された」値をチェックして、コストのかかる操作を実行するかどうかを判断できます。もちろん、このコード自体には問題があります。変更されたすべての行で列の少なくとも 1 つの値が変更されたことを伝えるだけだからです。

次のようにして、個々の UPDATE ステートメントをテストできます。

UPDATE Table SET Col1 = CASE WHEN i.Col1 = d.Col1 
          THEN Col1 
          ELSE dbo.fnTransform(Col1) END
FROM Inserted i
    INNER JOIN Deleted d ON i.Table_ID = d.Table_ID

... しかし、ストアド プロシージャを呼び出す必要がある場合には、この方法はうまく機能しません。そのような場合には、私の知る限り、他の方法に頼る必要があります。

私の質問は、トリガー内のデータベース操作を、変更された行の特定の列の値が実際に変更されたかどうかに基づいて予測するという問題に対する最良/最も安価なアプローチが何であるかについて、洞察力 (または、さらに良いのは確かなデータ) を持っている人がいるかどうかです。上記の方法はどちらも理想的とは思えないので、もっと良い方法があるのではないかと考えていました。

ベストアンサー1

まず、私は決してトリガー内でストアド プロシージャを呼び出すことはありません。複数行の挿入を考慮すると、プロシージャ内をカーソルで移動する必要があります。つまり、セットベースのクエリ (すべての価格を 10% 更新するなど) でロードした 200,000 行は、トリガーが負荷を懸命に処理しようとするため、何時間もテーブルをロックする可能性があります。さらに、プロシージャ内で何かが変更されると、テーブルへの挿入がまったく中断されたり、テーブルが完全にハングアップしたりする可能性があります。トリガー コードはトリガーの外部で何も呼び出すべきではないと私は固く信じています。

個人的には、単純にタスクを実行することを好みます。トリガーで実行したいアクションを適切に記述しておけば、列が変更された部分のみ更新、削除、または挿入が行われます。

例: パフォーマンス上の理由から非正規化が行われているため、2 か所に保存されている last_name フィールドを更新するとします。

update t
set lname = i.lname
from table2 t 
join inserted i on t.fkfield = i.pkfield
where t.lname <>i.lname

ご覧のとおり、更新中のテーブルに現在ある lname と異なる lname のみが更新されます。

監査を行い、変更された行のみを記録する場合は、i.field1 <> d.field1 または i.field2 <> d.field3 (すべてのフィールドを通じてなど) のように、すべてのフィールドを使用して比較を実行します。

おすすめ記事