行。我正在对表中的单行进行更新。 除主键外,所有字段都将被新数据覆盖。 但是,并非所有值都会更改更新的b / c。 例如,如果我的表格如下:
TABLE (id int ident, foo varchar(50), bar varchar(50))
初始值为:
id foo bar
-----------------
1 hi there
然后我执行UPDATE tbl SET foo = 'hi', bar = 'something else' WHERE id = 1
我想知道的是哪个列的值已更改,其原始值是什么以及它的新值是什么。
在上面的示例中,我希望看到“bar”列已从“there”更改为“其他内容”。
可以不进行逐列比较吗?是否有一些优雅的SQL语句,如EXCEPT,它将比行更精细?
感谢。
答案 0 :(得分: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
如果您因为这些更改而尝试做某事,那么最好写一个触发器:
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
(当然你可能想要在触发器中做更多的事情,但是有一个非常简单的动作的例子)
您可以使用COLUMNS_UPDATED
代替UPDATE
,但我觉得它很痛苦,而且它仍然不会告诉您哪些列实际更改了,哪些列是包含在UPDATE
声明中。例如,您可以编写UPDATE MyTable SET Col1 = Col1
,它仍然会告诉您Col1
已更新,即使实际上没有一个值发生更改。在编写触发器时,您需要实际测试单个前后值,以确保您获得真正的更改(如果这是您想要的)。
P.S。你也可以像Rob说的那样UNPIVOT
,但你仍然需要在UNPIVOT
子句中明确指定列,这不是魔术。
答案 1 :(得分:2)
尝试对已插入和已删除的内容进行解除隐藏,然后您可以加入,查找值已更改的位置。
答案 2 :(得分:1)
您可以在触发器中检测到这一点,或在SQL Server 2008中使用CDC。
如果您create a trigger FOR AFTER UPDATE
,则inserted
表将包含具有新值的行,deleted
表将包含具有旧值的相应行。
答案 3 :(得分:0)
OUTPUT deleted.bar AS [OLD VALUE], inserted.bar AS [NEW VALUE]
@Calvin我只是基于UPDATE示例。我并不是说这是完整的解决方案。我提示你可以在代码中的某个地方执行此操作; - )
由于我已经从上面的答案得到-1,让我把它放在:
如果您真的不知道哪个列已更新,我会说创建一个触发器并在该触发器的主体中使用COLUMNS_UPDATED()函数(参见this)
我在我的博客中创建了一个Bitmask Reference,用于此COLUMNS_UPDATED()。如果您决定遵循此路径,它将使您的生活更轻松(Trigger + Columns_Updated())
如果你不熟悉触发器,这是我的基本触发器示例http://dbalink.wordpress.com/2008/06/20/how-to-sql-server-trigger-101/
答案 4 :(得分:0)
如果您使用的是SQL Server 2008,则应该查看新的“更改数据捕获”功能。这将做你想要的。
答案 5 :(得分:0)