在插入和删除的表上整形SELECT语句的XML输出

时间:2012-04-24 15:24:47

标签: sql sql-server xml tsql sql-server-2008-r2

我正在捕获触发器中inserteddeleted表的内容,并将它们作为xml保存到更改日志中。

对于插入和删除它是直接的。只需在相应的表上进行选择,然后使用FOR XML将其转换为xml。

更新变得有点棘手,因为您正在合并来自两个表的数据,这两个表基本上代表之前的之后的状态{{1 }}。现在,我正在使用UPDATEUNIONinserted中的数据合并到一个结果集中。

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>

但我不知道如何做到这一点。有什么想法吗?

注意:我们的一些客户仍在使用SQL Server 2005,因此使用新的更改跟踪确实不是一个选项。

3 个答案:

答案 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 :(得分:2)

我已经完成了这个审计跟踪解决方案,并遇到了同样的问题。我的决议如下:

正如您所提到的,插入和删除很容易。插入新记录时,我们不存储任何XML,因为插入中包含的字段值是实时表中的当前值。删除记录时,将记录的状态存储在XML字段中删除的位置。

更新带来了一些问题,因为我不想为每条记录(即之前和之后)存储两次数据。然后我发现我只需要存储“Before”数据(即删除的表),因为“After”存储在原始表本身中,或者下面的审计跟踪记录存储在同一记录中。它在重建路径时确实让生活变得更加困难,但从存储的角度来看确实有意义。

换句话说,假设在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中记录的前后状态。我们的样本记录的审计跟踪如下:

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仅记录其值已更改的字段。这意味着我们无法立即确定历史中两点之间的更改,而无需迭代其间的所有日志。但是,由于这些日志很少被查看,因此优先考虑使它们高效而不是快速。这取决于您特定环境的要求。

我使用的查询示例如下所示:

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'

答案 2 :(得分: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