2012-04-24 11 views
1

私はinserteddeletedテーブルの内容をトリガーでキャプチャし、xmlとして変更ログに保存します。挿入されたテーブルと削除されたテーブルのSELECT文のXML出力を整形する

すぐに挿入して削除します。対応するテーブルを選択してFOR XMLを使用してxmlに変換してください。

アップデートでは、基本的にUPDATEの状態の後前を表す2つのテーブルからデータをマージしているので、もう少しトリッキーであることを輩出しています。今はUNIONを使ってinserteddeletedのデータを1つの結果セットに結合しています。

<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> 

しかし、私は:私は出力がなりたい何

<!-- 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> 

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

出力は次のようになりますこれを達成する方法がわからない何か案は?

注:一部のお客様のクライアントではまだSQL Server 2005が使用されているため、新しい変更の追跡は実際にはオプションではありません。

+0

あなたは上のテーブルを結合することができ、主キーを持っていますか:

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

+0

はい。私はこのトリガーを複数のテーブルに適用する必要がありますが、それらはすべて同じように定義された主キーを持っています。 –

答えて

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') 
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 
+0

'inserted'と' updated'の間にクロス結合があります。一度に1つの行のみを更新する場合はうまく動作します。更新ステートメントが更新された行ごとに一度ではなく一度だけ呼び出されるため、更新ステートメントが複数の行に影響を与えた場合にはそれほど優れていません。 –

+0

@Mikael:良​​い点。私はトリガに精通していることに精通していない、私はちょうどXMLを形作る方法を示していた。 – weenoid

+0

@Mikael:更新されました。 – weenoid

2

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

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

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

つまり、レコードが01:00:00に追加されたとします。レコードは02:00:00と02:30:00に更新され、03:00:00に削除されます。あなたはMyTableというのレコードの前と後の状態を示す2つの連続したレコードを取得することができ、自己結合を使用して

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 

:あなたの監査証跡は次のようになります。次のように私たちのサンプルレコードの監査証跡は次のとおりです。

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' 
関連する問題