比较SQL Server 2008中已删除和已插入的表

时间:2014-04-25 07:21:16

标签: sql sql-server sql-server-2008 audit-trail

我是SQL Server 2008的新手,我需要大家的建议。我想在SQL Server 2008的inserteddeleted表中查找已更改的值,因为我正在执行审计跟踪以保留旧值和新值。如何循环所有列以找出已删除和插入的表中哪个字段的值更改?我曾尝试使用if else语句进行比较

例如:

create trigger trg_XXX on dbo.table
after update
as
begin
    declare 
       @oldID varchar(6),
       @newID varchar(6)

    select @oldID = ID from deleted
    select @newID = ID from inserted

    if(@oldID != @newID)
       insert into table (a, b) values (@oldID, @newID)
    else
       print('do nothing')

有没有办法使用游标循环删除和插入的表格或任何替代方式?能给我一些例子吗?

4 个答案:

答案 0 :(得分:4)

我不太确定,你的目标是什么,我认为它可能是这样的。 假设我们有一个这样的表:

CREATE TABLE Product
(
     ID      INT                   PRIMARY KEY,
     Name    NVARCHAR(100)         NOT NULL,
     Price   DECIMAL(10,2)         NOT NULL
);

和一些像这样的审计表:

CREATE TABLE ProductAudit
(
     AuditID      INT                   IDENTITY PRIMARY KEY, 
     ProductID    INT                   NOT NULL
     OldName      NVARCHAR(100)         NULL,
     OldPrice     DECIMAL(10,2)         NULL,
     NewName      NVARCHAR(100)         NULL,
     NewPrice     DECIMAL(10,2)         NULL
);

然后你创建一个触发器:

CREATE TRIGGER TR_AUDIT
ON Product
FOR INSERT, UPDATE, DELETE
AS
BEGIN
       INSERT INTO ProductAudit (ProductID, OldName, OldPrice, NewName, NewPrice)
       SELECT 
           COALESCE(I.ID, D.ID),
           D.Name,
           D.Price,
           I.Name,
           I.Price
       FROM 
           INSERTED I FULL OUTER JOIN DELETED D ON I.ID = D.ID;
END
GO

你有它。

答案 1 :(得分:2)

我认为您正在寻找SQL服务器中非正式称为magic tables的内容。

表格“INSERTED”和“DELETED”被称为魔术表 SQL Server。我们无法在数据库中看到这些表。但我们可以访问这些 来自“TRIGGER

的表格

当我们将记录插入表格时,将创建魔术表“INSERTED” 在该表中,当前插入的行将可用。我们可以访问它 记录在“TRIGGER”中。

当我们更新创建触发器的表上的记录时,将创建魔术表“INSERTED”和“DELETED”两者,更新记录的旧数据将在“DELETED”表,新数据将在“INSERTED”表中提供,同时在触发器内访问它们。

当我们从表中删除记录时,将创建魔术表“DELETED” 在该表中,当前删除的行将可用。我们可以访问它 记录在“TRIGGER”。

示例:

以下代码解释了魔术表“INSERTED”:

CREATE TRIGGER LogMessage
ON EMP
FOR INSERT
AS
   DECLARE @EMPNAME varchar(50)
   SELECT @EMPNAME= (SELECT EMPNAME FROM INSERTED)
   INSERT INTO LOGTABLE(UserId,Message) values (@EMPNAME,'Record Added')
GO

以下代码解释魔术表“DELETED

CREATE TRIGGER LogMessage
ON EMP
FOR DELETE
AS
   DECLARE @EMPNAME varchar(50)
   SELECT @EMPNAME= (SELECT EMPNAME FROM DELETED)
   INSERT INTO LOGTABLE(UserId,Message) values (@EMPNAME,'Record Removed')
GO

来源(这些文章和作者的所有功劳):

http://www.codeproject.com/Questions/285423/what-is-magic-table-different-types-of-magic-table http://www.dotnetspider.com/resources/29332-Magic-tables-SQL-Server.aspx

<强>替代地

,您可以尝试: Obtaining Changes by Using the Change Tracking Functions,其MSDN链接解释如何 使用内置函数跟踪更改。

CHANGETABLE(CHANGES …)功能

此行集功能用于查询更改信息。该函数查询存储在内部更改跟踪表中的数据。该函数返回一个结果集,其中包含已更改的行的主键以及其他更改信息,例如操作,更新列和行的版本。

CHANGE_TRACKING_CURRENT_VERSION()功能

用于获取下次查询更改时将使用的当前版本。此版本表示上次提交的事务的版本。

<强> CHANGE_TRACKING_MIN_VALID_VERSION()功能

用于获取客户端可以拥有的最小有效版本,并且仍然可以从CHANGETABLE()获取有效结果。客户端应根据此函数返回的值检查上次同步版本。如果上一个同步版本小于此函数返回的版本,则客户端将无法从CHANGETABLE()获取有效结果,并且必须重新初始化。

参考语法&amp;用于http://technet.microsoft.com/en-us/library/cc280358%28v=sql.105%29.aspx

希望它有所帮助。

答案 2 :(得分:1)

首先:不要在触发器内使用光标 - 永远!

第二:确定您可以使用的更新语句中包含哪些字段: UPDATE()COLUMNS_UPDATED()

注意:这不会列出已更改其值的字段,只列出UPDATE语句的SET部分中包含的列列表。

第三:您可以使用多种方法来审核对表格的更改(Most efficient method to detect column change in MS SQL Server上接受的答案有一个很好的列表和指导如何使用,如果您正在使用SQL Server Enterprise,您还可以使用Change Data Capture

查看

我将使用的一些示例审计代码(我希望按列审计表记录列):

INSERT INTO AuditTable (ColumnChanged, OldValue, NewValue) /* I assume there are default columns logging who/when the change was done by? */
    SELECT 'ColumnA' as ColumnChanged, d.ColumnA, i.ColumnA
    FROM inserted i join deleted d ON d.PKID = i.PKID
    WHERE 
        /* both aren't null and the value has changed */
        (d.ColumnA IS NOT NULL AND i.ColumnA IS NOT NULL AND d.ColumnA != i.ColumnA) 
        /* it was null and now it isn't */
        OR (d.ColumnA IS NULL AND i.ColumnA IS NOT NULL) 
        /* it wasn't null and now it is */
        OR (d.ColumnA IS NOT NULL AND i.ColumnA IS NULL)
UNION 
    SELECT 'ColumnB' as ColumnChanged, d.ColumnB, i.ColumnB
    FROM inserted i join deleted d ON d.PKID = i.PKID
    WHERE 
        /* both aren't null and the value has changed */
        (d.ColumnB IS NOT NULL AND i.ColumnB IS NOT NULL AND d.ColumnB != i.ColumnB) 
        /* it was null and now it isn't */
        OR (d.ColumnB IS NULL AND i.ColumnB IS NOT NULL) 
        /* it wasn't null and now it is */
        OR (d.ColumnB IS NOT NULL AND i.ColumnB IS NULL)
....  /* continuing for each column */

它会更简单(从sql的角度来看,更快[由于更少的写入]按行审计),即:

INSERT INTO AuditTable (OldValueA, NewValueA, OldValueB, NewValueB) 
SELECT d.ColumnA, i.ColumnA, d.ColumnB, i.ColumnB
FROM inserted i join deleted d ON d.PKID = i.PKID
WHERE 
/* same check for columnA */
    /* both aren't null and the value has changed */
    (d.ColumnA IS NOT NULL AND i.ColumnA IS NOT NULL AND d.ColumnA != i.ColumnA) 
    /* it was null and now it isn't */
    OR (d.ColumnA IS NULL AND i.ColumnA IS NOT NULL) 
    /* it wasn't null and now it is */
    OR (d.ColumnA IS NOT NULL AND i.ColumnA IS NULL)
/* now check columnB */
    (d.ColumnB IS NOT NULL AND i.ColumnB IS NOT NULL AND d.ColumnB != i.ColumnB) 
    OR (d.ColumnB IS NULL AND i.ColumnB IS NOT NULL) 
    OR (d.ColumnB IS NOT NULL AND i.ColumnB IS NULL)
....  /* continuing for each column */

答案 3 :(得分:0)

您可以通过将插入和删除的表转换为2个xml,遍历属性并比较xml值来进行动态“比较”。

示例:

CREATE TRIGGER MY_COMPARER
   ON TABLE_NAME
   AFTER UPDATE
AS
BEGIN
    DECLARE @columnIndex INT = 1;
    DECLARE @maxColumns INT = (select count(*) from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TABLE_NAME');
    DECLARE @insertedXML XML = (select top 1 * from INSERTED FOR XML PATH('ROOT'), ELEMENTS XSINIL);
    DECLARE @deletedXML XML = (select top 1 * from DELETED FOR XML PATH('ROOT'), ELEMENTS XSINIL);

    WHILE @columnIndex <= @maxColumns BEGIN 
        DECLARE @insertedXMLValue XML = (select @insertedXML.query('/ROOT/*[sql:variable("@columnIndex")]'));
        DECLARE @deletedXMLValue XML = (select @deletedXML.query('/ROOT/*[sql:variable("@columnIndex")]'));

        DECLARE @insertedValue NVARCHAR(MAX) = CONVERT(NVARCHAR(MAX), @insertedXMLProp);
        DECLARE @deletedValue NVARCHAR(MAX) = CONVERT(NVARCHAR(MAX), @deletedXMLProp);

        IF (@insertedValue != @deletedValue)
            print('Column: ' + CONVERT(NVARCHAR(MAX), @columnIndex) + ' has changed')

        SET @columnIndex = @columnIndex + 1;
    END
END