2

我是 SQL Server 2008 的新手,我需要大家的建议。我想找出一个表的 SQL Server 2008inserteddeleted表中的更改值,因为我目前正在执行审计跟踪以保留旧值和新值。如何循环所有列以找出删除和插入表中哪个字段的值更改?我曾尝试使用 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

4 回答 4

5

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

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

你有它。

于 2014-04-25T07:44:11.203 回答
2

我认为您正在寻找magic tablesSQL Server 中非官方调用的内容。

表“ 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 -服务器.aspx

. .

或者

,您可以尝试: 使用更改跟踪函数获取更改,其 MSDN 链接解释了如何使用内置函数跟踪更改。

CHANGETABLE(CHANGES …)功能

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

CHANGE_TRACKING_CURRENT_VERSION()功能

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

CHANGE_TRACKING_MIN_VALID_VERSION()功能

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

请参阅http://technet.microsoft.com/en-us/library/cc280358%28v=sql.105%29.aspx上的语法和用法

希望能帮助到你。

于 2014-04-25T07:29:48.913 回答
2

您可以通过将插入和删除的表转换为 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
于 2019-04-16T12:10:38.107 回答
1

第一:不要在触发器内使用游标 - 永远!

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

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

第三:您可以使用多种方法来审核对表的更改(如果您使用 SQL,在 MS SQL Server 中检测列更改的最有效方法上的公认答案有一个很好的列表和使用指南) 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 */
于 2014-04-25T07:49:31.357 回答