1

insertedおよびテーブルの内容をトリガーでキャプチャし、deletedそれらを xml として変更ログに保存しています。

挿入と削除の場合は、まっすぐに進みます。対応するテーブルを選択し、それを使用FOR XMLして xml に変換するだけです。

基本的に の前後の状態を表す2 つのテーブルからデータをマージしているため、更新は少し複雑であることが判明していますUPDATE。現在UNION、データを単一の結果セットに結合するために a をinserted使用deletedしています。

SELECT [Column1], [Column2]
FROM
    (SELECT [Column1], [Column2]
     FROM inserted
     UNION
     SELECT [Column], [Column2]
     FROM deleted) as Rows
FOR XML RAW, ELEMENTS

出力は次のようになります。

<!-- Comments added for clarity. They don't appear in the actual output -->
<row> <!-- After -->
    <Column1>Bar</Column1>
    <Column2>Ipsum</Column2>
</row>
<row> <!-- Before -->
    <Column1>Foo</Column1>
    <Column2>Lorem</Column2>
</row>

私が出力したいのは次のとおりです。

<row>
    <Column1>
        <Before>Foo</Before>
        <After>Bar</After>
    </Column1>
    <Column2>
        <Before>Lorem</Before>
        <After>Ipsum</After>
    </Column2>
</row>

または多分

<row>
    <Column1 Before="Foo" After="Bar" />
    <Column2 Before="Lorem" After="Ipsum" />
</row>

しかし、これを達成する方法がわかりません。何か案は?

注: 一部のクライアントはまだ SQL Server 2005 を使用しているため、新しい変更追跡を使用するオプションはまだありません。

4

3 に答える 3

2

これを試すことができます。通常のテーブルでテストしました(トリガーではありません)。そして、PKが呼び出されたと仮定しIDます。

select d.Column1 as 'Column1/Before',
       i.Column1 as 'Column1/After',
       d.Column2 as 'Column2/Before',
       i.Column2 as 'Column2/After'
from inserted as i
  inner join deleted as d
    on i.ID = d.ID
for xml path('row')
于 2012-04-24T16:06:10.183 に答える
2

私は監査証跡ソリューションのためにこれを正確に行い、同じ問題に遭遇しました。私の決意は次のとおりでした。

あなたが言ったように、挿入と削除は簡単です。新しいレコードが挿入されるとき、挿入に含まれるフィールド値はライブ テーブルの現在の値であるため、XML は保存されません。レコードが削除されると、削除された時点でのレコードの状態が XML フィールドに保存されます。

更新には少し問題がありました。各レコードに対してデータを 2 回保存する必要がなかったからです (つまり、更新前と更新後)。次に、「変更前」のデータ (つまり、削除されたテーブル) のみを保存する必要があることに気付きました。これは、「変更後」のデータが元のテーブル自体、または同じレコードに対する次の監査証跡レコードに格納されているためです。トレイルを再構築するときは少し難しくなりますが、ストレージの観点からは確かに理にかなっています.

つまり、01:00:00 にレコードが追加されたとします。レコードは 02:00:00 と 02:30:00 に更新され、03:00:00 に削除されます。監査証跡は次のようになります。

AuditKey Timestamp  RecordType    RecordKey   AuditType    XML
1        01:00:00   MyTable       213         I            NULL
2        02:00:00   MyTable       213         U            XML1
3        02:30:00   MyTable       213         U            XML2
4        03:00:00   MyTable       213         D            XML3

自己結合を使用すると、MyTable 内のレコードの前後の状態を示す 2 つの連続したレコードを取得できます。サンプル レコードの監査証跡は次のとおりです。

From AuditKey 1 and 2: Record was inserted at 01:00, with the field values XML1
From AuditKey 2 and 3: Record was updated at 02:00, and changed from XML1 to XML2
From AuditKey 3 and 4: Record was updated at 02:30, and changed from XML2 to XML3
From AuditKey 4 and NULL: Record was deleted at 03:00

最後のレコードが削除ではなく別の更新である場合、監査証跡の最後の行は次のようになります。

From AuditKey 4 and NULL: Record was updated at 03:00, and changed from XML3 to the field values in the live table.

このアプローチは私たちにとってうまく機能し、歴史の任意の時点まで記録を再構築できるようになりました。このアプローチをさらに一歩進めると、各レコードに対して格納される XML は、値が変更されたフィールドのみを記録します。これは、その間のすべてのログを反復処理しない限り、履歴の 2 つのポイント間で何が変更されたかをすぐに判断できないことを意味します。ただし、これらのログはめったに表示されないため、迅速ではなく効率的にすることが優先されました。これは、特定の環境の要件によって異なります。

私が使用したクエリのサンプルは次のようになります。

WITH cte AS (SELECT ROW_NUMBER() OVER (ORDER BY [Timestamp]) AS RowNum,
    AuditKey, [Timestamp], AuditType, XML FROM AuditTrail 
    WHERE RecordType = 'MyTable' AND RecordKey = 213)
SELECT t1.Timestamp, t1.AuditType, t1.XML AS FromXML, 
    CASE WHEN t2.XML IS NULL THEN (SELECT * FROM MyTable WHERE RecordKey = 213 FOR XML AUTO) ELSE t2.XML END AS ToXML
FROM cte t1 LEFT JOIN cte t2 ON t2.AuditKey = t1.AuditKey + 1
WHERE t2.AuditType <> 'D'
于 2012-04-24T16:57:21.643 に答える
1
SELECT      CAST('
<row>
  <Column1 Before="' + i.Column1 + '" After="' + d.Column1 + '" />
  <Column2 Before="' + i.Column2 + '" After="' + d.Column2 + '" />
</row>' AS xml)
FROM        inserted i
INNER JOIN  deleted d ON i.ID = d.ID
于 2012-04-24T16:07:02.857 に答える