T-SQL:更新后COLUMNS发生了哪些变化?

Cal*_*vin 5 t-sql sql-server sql-server-2005

好.我正在对表中的单行进行更新.除主键外,所有字段都将被新数据覆盖.但是,并非所有值都会更改更新的b/c.例如,如果我的表格如下:

TABLE (id int ident, foo varchar(50), bar varchar(50))
Run Code Online (Sandbox Code Playgroud)

初始值是:

id   foo   bar
-----------------
1    hi    there
Run Code Online (Sandbox Code Playgroud)

然后我执行 UPDATE tbl SET foo = 'hi', bar = 'something else' WHERE id = 1

我想知道的是哪个列的值已更改,其原始值是什么以及它的新值是什么.

在上面的例子中,我希望看到列"bar"从"there"更改为"something else".

可以不进行逐列比较吗?是否有一些优雅的SQL语句,如EXCEPT,将比行更细粒度?

谢谢.

Aar*_*ght 9

没有可以运行的特殊语句可以准确地告诉您哪些列已更改,但是查询并不难写:

DECLARE @Updates TABLE
(
    OldFoo varchar(50),
    NewFoo varchar(50),
    OldBar varchar(50),
    NewBar varchar(50)
)

UPDATE FooBars
SET <some_columns> = <some_values>
OUTPUT deleted.foo, inserted.foo, deleted.bar, inserted.bar INTO @Updates
WHERE <some_conditions>

SELECT *
FROM @Updates
WHERE OldFoo != NewFoo
OR OldBar != NewBar
Run Code Online (Sandbox Code Playgroud)

如果您尝试通过这些更改实际执行某些操作,那么最好编写一个触发器:

CREATE TRIGGER tr_FooBars_Update
ON FooBars
FOR UPDATE AS
BEGIN
    IF UPDATE(foo) OR UPDATE(bar)
        INSERT FooBarChanges (OldFoo, NewFoo, OldBar, NewBar)
            SELECT d.foo, i.foo, d.bar, i.bar
            FROM inserted i
            INNER JOIN deleted d
                ON i.id = d.id
            WHERE d.foo <> i.foo
            OR d.bar <> i.bar
END
Run Code Online (Sandbox Code Playgroud)

(当然你可能想在触发器中做更多的事情,但是有一个非常简单的动作的例子)

您可以使用COLUMNS_UPDATED而不是UPDATE但我觉得它很痛苦,它仍然不会告诉您哪些列实际更改了,只是哪些列包含在UPDATE语句中.因此,例如,您可以编写UPDATE MyTable SET Col1 = Col1,它仍会告诉您Col1已更新,即使实际上没有一个值更改.在编写触发器时,您需要实际测试单个前后值,以确保您获得真正的更改(如果这是您想要的).

PS你也可以UNPIVOT像Rob说的那样,但是你仍然需要在UNPIVOT子句中明确指定列,这不是魔术.