2

次のようなINSERTEDテーブルとDELETEDテーブルを生成するUPDATEトリガーがあります。

挿入

Id  Name    Surname
1   Stack   Overflow
2   Luigi   Saggese

削除済み

Id  Name    Surname
1   Stacks  Overflow
2   Luigi   Sag

この更新をログテーブルにキャプチャしたいと思います。私のログテーブル(すべてのテーブルに対してグローバル)は次のようになります(次に、INSERTEDテーブルとDELETEDテーブルを処理する必要があります)。

Id_Table    Table_Key_Value   Id_Value   Old_Value  New_Value
12345               1          4556645    Stack      Stacks
12345               1           544589   Overflow   Overflows
12345               2           544589   Saggese       Sag

Id_Tableは、UPDATEステートメントを実行したテーブルのシステムobject_idであり、UPDATEされたTable_Key_Value is列の主キーの値は、Id_Value各テーブルの各列にマップしたカスタムIDです。列のデータは、列がUPDATEによって変更された場合にのみログに記録されます。

私はこれを行う2つの方法を考えました:

  1. 列ごとに1回、テーブルでSELECTを実行します。

    INSERT INTO LOG (Id_Table, Table_Key_Value, Id_Value,Old_Value, New_Value)
       SELECT 12345, Id, 4556645, D.Name, I.Name
       FROM INSERTED I 
       INNER JOIN DELETED D ON I.ID = D.ID
       WHERE D.Name <> I.Name
    
       union
    
       SELECT 12345, Id, 544589, D.Surname, I.Surname
       FROM INSERTED I 
       INNER JOIN DELETED D ON I.ID = D.ID
       WHERE D.Surname <> I.Surname
    
  2. UDFに対して単一選択を実行する:

    SELECT CustomFunction(12345,Id, I.Name, D.Name, I.Surname, D.Surname) 
    FROM INSERTED I  
    INNER JOIN DELETED D ON I.ID = D.ID
    
    **CustomFunction** (_Id_Table,_Table_Key_Value, _Old_Value_Name, _New_Value_Name, _Old_Value_Surname, _New_Value_Surname)
    
    INSERT INTO LOG(Id_Table, Table_Key_Value, Id_Value,Old_Value, New_Value)
    VALUES(_Id_Table,_Table_Key_Value, 4556645, _Old_Value_Name, _New_Value_Name)
    
    INSERT INTO LOG(Id_Table, Table_Key_Value, Id_Value,Old_Value, New_Value)
    VALUES(_Id_Table,_Table_Key_Value, 544589, _Old_Value_Surname, _New_Value_Surname)
    

これを行う他の方法はありますか?最も効率的で保守可能な方法は何ですか?

4

1 に答える 1

3

答える前に、すべてのテーブルを単一のテーブルに記録するのが最善だとは思わないことを最初に言わせてください。データベースが大きくなると、ログ テーブルで深刻な競合が発生する可能性があります。さらに、同じ列に配置するには、すべてのデータを varchar または sql_variant に変更する必要があり、より多くのスペースを必要とします。また、更新された各列を個別の行に記録する (更新されていない列をスキップする) と、クエリが非常に難しくなると思います。そのすべてのデータをまとめて、各行の変更、いつ、誰による変更の複合的で適切なビューを実際に取得する方法を知っていますか? 私の意見では、テーブルごとに 1 つのログ テーブルを持つことは、はるかに簡単になるでしょう。そうすれば、それを機能させようとして経験している問題はなくなります。

また、SQL Server 2008 Change Data Captureについてご存知でしたか? SQL Server の Enterprise または Developer エディションを使用している場合は、代わりにそれを使用してください。

その問題とは別に、論理的な UNPIVOT (独自のバージョンを実行する) を使用して、必要なことを行うことができます。ターゲット列が 1 つではなく 2 つあるため、ネイティブ SQL 2005 UNPIVOT を実際に使用することはできません。CROSS APPLY を使用して UNPIVOT を実行する SQL Server 2005 以降の例を次に示します。

INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT 12345, I.Id, X.Id_Value, X.Old_Value, X.New_Value
FROM
   INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   CROSS APPLY (
      SELECT 4556645, D.Name, I.Name
      UNION ALL SELECT 544589, D.Surname, I.Surname
    ) X (Id_Value, Old_Value, New_Value)
WHERE
   X.Old_Value <> X.New_Value

SQL 2000 またはその他の DBMS のより一般的な方法を次に示します (理論的には、Oracle、MySQL などで動作するはずです。OracleFROM DUALは、派生テーブルの各 SELECT に追加します)。

INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT *
FROM (
   SELECT
      12345,
      I.Id,
      X.Id_Value,
      CASE X.Id_Value
         WHEN 4556645 THEN D.Name
         WHEN 544589 THEN D.Surname
      END Old_Value,
      CASE X.Id_Value
         WHEN 4556645 THEN I.Name
         WHEN 544589 THEN I.Surname
      END New_Value   
   FROM
      INSERTED I 
      INNER JOIN DELETED D ON I.ID = D.ID
      CROSS JOIN (
         SELECT 4556645
         UNION ALL SELECT 544589
      ) X (Id_Value)
) Y
WHERE
   Y.Old_Value <> Y.New_Value

SQL Server 2005 以降にはネイティブの UNPIVOT コマンドがありますが、一般に、UNPIVOT が機能する場合でも、代わりに CROSS APPLY を使用するのが好きです。具体的には、UNPIVOT は 1 つの宛先列しかターゲットにできないため、ネイティブの UNPIVOT コマンドはここでは機能しませんが、2 つ (Old_Value、New_Value) が必要です。2 つの列を 1 つの値に連結する (後で分離する) のは適切ではありません。後でPIVOTに無意味な行相関値を作成するのは良くありません。これら2つのバリエーションではない別の方法は考えられません。CROSS APPLY ソリューションは、説明した正確なログ テーブル構造に一致させるのに最適です。

ここでの私のクエリと比較すると、あなたの方法 #1 はうまく機能しません (約 {列の数}:1 の比率でパフォーマンスが低下します)。あなたの方法#2は良い考えですが、UDFを呼び出すと大きなオーバーヘッドが発生し、さらに各行をループする必要があるため(震えます)、それでも最適ではありません。

于 2012-08-03T05:41:53.270 に答える