我是SQL Server 2008的新手,我需要大家的建议。我想在SQL Server 2008的inserted
和deleted
表中查找已更改的值,因为我正在执行审计跟踪以保留旧值和新值。如何循环所有列以找出已删除和插入的表中哪个字段的值更改?我曾尝试使用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')
有没有办法使用游标循环删除和插入的表格或任何替代方式?能给我一些例子吗?
答案 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