0

我找不到一种简单/通用的方法来注册审计表,在某些表上更改了列。

我尝试以这种方式在更新后使用触发器来做到这一点:

首先是审计表定义:

CREATE TABLE [Audit](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Date] [datetime] NOT NULL default GETDATE(),
[IdTypeAudit] [int] NOT NULL, --2 for Modify
[UserName] [varchar](50) NULL,
[TableName] [varchar](50) NOT NULL,
[ColumnName] [varchar](50) NULL,
[OldData] [varchar](50) NULL,
[NewData] [varchar](50) NULL )

接下来是任何表中的 AFTER UPDATE 触发器:

DECLARE 
    @sql varchar(8000),
    @col int,
    @colcount int

select @colcount = count(*) from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'MyTable'
set @col = 1

while(@col < @colcount )
begin

    set @sql=
    'INSERT INTO Audit
    SELECT 2, UserNameLastModif, ''MyTable'', COL_NAME(Object_id(''MyTable''), '+ convert(varchar,@col) +'), Deleted.' 
    + COL_NAME(Object_id('MyTable'), @col) + ', Inserted.' + COL_NAME(Object_id('MyTable'), @col) + '
    FROM Inserted LEFT JOIN Deleted ON Inserted.[MyTableId] = Deleted.[MyTableId]
    WHERE COALESCE(Deleted.' + COL_NAME(Object_id('MyTable'), @col) + ', '''') <> COALESCE(Inserted.' + COL_NAME(Object_id('MyTable'), @col) + ', '''')'

    --UserNameLastModif is an optional column on MyTable
    exec(@sql)
    set @col = @col + 1

end

问题

  1. 当我使用 exec 函数时,Inserted 和 Deleted 丢失了上下文
  2. 似乎 colnumber 它并不总是一个相关数字,似乎如果您创建一个包含 20 列的表,然后删除一个并创建另一个,最后一个有一个数字 > @colcount

我一直在为整个网络寻找解决方案,但我无法弄清楚

任何想法?

谢谢!

4

3 回答 3

1

@Santiago:如果您仍想用动态 SQL 编写它,则应先准备所有语句,然后再执行它们。8000 个字符可能不足以满足所有语句。一个好的解决方案是使用表格来存储它们。

IF NOT OBJECT_ID('tempdb..#stmt') IS NULL
    DROP TABLE #stmt; 
CREATE TABLE #stmt (ID int NOT NULL IDENTITY(1,1), SQL varchar(8000) NOT NULL); 

然后将行替换exec(@sql)INSERT INTO #stmt (SQL) VALUES (@sql);

然后执行每一行。

WHILE EXISTS (SELECT TOP 1 * FROM #stmt)
BEGIN
    BEGIN TRANSACTION; 
        EXEC (SELECT TOP 1 SQL FROM #stmt ORDER BY ID); 
        DELETE FROM #stmt WHERE ID = (SELECT MIN(ID) FROM #stmt); 
    COMMIT TRANSACTION; 
END

请记住使用 sys.columns 进行列循环(我假设您使用 SQL 2005/2008)。

SET @col = 0; 
WHILE EXISTS (SELECT TOP 1 * FROM sys.columns WHERE object_id = OBJECT_ID('MyTable') AND column_id > @col) 
BEGIN
    SELECT TOP 1 @col = column_id FROM sys.columns 
    WHERE object_id = OBJECT_ID('MyTable') AND column_id > @col ORDER BY column_id ASC; 
    SET @sql ....
    INSERT INTO #stmt ....
END

删除第 4 行@colcount int和前面的逗号。删除信息架构选择。

于 2011-10-07T22:07:37.267 回答
1

永远不要使用任何类型的循环触发器。不要使用动态 SQl 或调用存储过程或发送电子邮件。所有这些事情在触发器中都是非常不合适的。

如果你想使用动态 sql 使用它来创建脚本来创建触发器。并为您要审核的每个表创建一个审核表(实际上每个表都有两个),否则您将因锁定“一个表来统治所有表”而遇到性能问题。

于 2011-10-07T22:13:26.157 回答
1

这凸显了结构选择的更大问题。尝试编写基于集合的解决方案。删除循环和动态 SQL 并编写插入审计行的单个语句。这是可能的,但为了更容易考虑不同的表格布局,例如将所有列保留在 1 行而不是拆分它们。

在 SQL 2000 中使用 syscolumns。在 SQL 2005+ 中使用 sys.columns。IE

SELECT column_id FROM sys.columns WHERE object_id = OBJECT_ID(DB_NAME()+'.dbo.Table'); 
于 2011-10-06T23:52:31.780 回答