而不是在所有表上触发插入

时间:2011-07-01 17:57:24

标签: sql sql-server-2008

有人可以提供有关如何为数据库中的所有表编写插入通用而不是触发器的源代码。我想运行存储过程,它将为db中的所有表创建而不是insert触发器。

2 个答案:

答案 0 :(得分:1)

在没有准确理解为什么你想要在每个表上使用触发器而不是触发器的情况下,除了将提供的值插入基表之外,你还想在结果代码中做什么?(就像没有发生的情况一样)而不是触发器,这是我想出的。您会注意到,如果触发器已存在,它将触发该触发器,因此您可以在同一数据库中多次运行此触发器,而不会出现“已存在”错误。它忽略IDENTITY,ROWGUIDCOL,计算列和TIMESTAMP / ROWVERSION列。最后,我将展示如何快速检查,而不是执行(注释掉)输出脚本(最多8K),如果想要查看更多(最多64K),则将其转换为XML。无法保证您可以在SSMS中返回整个内容,具体取决于有多少表/列。如果你想检查它和/或手动运行它你可能想创建一个存储值的表 - 然后你可以用一个应用程序或你有什么把它拉出来。现在,如果您希望自动执行此操作,您可以遵循Yuck的观点 - 将其保存为存储过程并创建响应某些DDL事件的DDL触发器(CREATE TABLE等)。

SET NOCOUNT ON;

DECLARE 
    @cr VARCHAR(2) = CHAR(13) + CHAR(10),
    @t  VARCHAR(1) = CHAR(9),
    @s  NVARCHAR(MAX) = N'';

;WITH t AS
(
    SELECT [object_id], 
     s = OBJECT_SCHEMA_NAME([object_id]),
     n = OBJECT_NAME([object_id])
    FROM sys.tables WHERE is_ms_shipped = 0     
)
SELECT @s += 'IF OBJECT_ID(''dbo.ioTrigger_' + t.s + '_' + t.n + ''') IS NOT NULL
    DROP TRIGGER [dbo].[ioTrigger_' + t.s + '_' + t.n + '];
G' + 'O
CREATE TRIGGER ioTrigger_' + t.s + '_' + t.n + '
    ON ' + QUOTENAME(t.s) + '.' + QUOTENAME(t.n) + '
    INSTEAD OF INSERT
AS 
BEGIN
    SET NOCOUNT ON;

    -- surely you must want to put some other code here?

    INSERT ' + QUOTENAME(t.s) + '.' + QUOTENAME(t.n) + '
    (
' + 
(
    SELECT @t + @t + name + ',' + @cr
        FROM sys.columns AS c
        WHERE c.[object_id] = t.[object_id]
        AND is_identity = 0
        AND is_rowguidcol = 0
        AND is_computed = 0
    AND system_type_id <> 189
    FOR XML PATH(''), TYPE
).value('.[1]', 'NVARCHAR(MAX)') + '--' 
+ @cr + @t + ')'
+ @cr + @t + 'SELECT 
' + 
(
    SELECT @t + @t + name + ',' + @cr
        FROM sys.columns AS c
        WHERE c.[object_id] = t.[object_id]
        AND is_identity = 0
        AND is_rowguidcol = 0
        AND is_computed = 0
    AND system_type_id <> 189
    FOR XML PATH(''), TYPE
).value('.[1]', 'NVARCHAR(MAX)') + '--'
+ @cr + @t + 'FROM
        inserted;
END' + @cr + 'G' + 'O' + @cr
FROM t
ORDER BY t.s, t.n;

SELECT @s = REPLACE(@s, ',' + @cr + '--' + @cr, @cr);

-- you can inspect at least part of the script by running the
-- following in text mode:

SELECT @s;

-- if you want to see more of the whole thing (but not necessarily
-- the whole thing), run this in grid mode and click on result:

SELECT CONVERT(XML, @s);

有几点需要注意:

1)我不处理稀疏列,xml集合,文件流等。所以如果你有精美的表格,你可能会遇到其中一些功能的并发症。

2)触发器的名称实际上并不是“受保护的” - 你可以有一个名为foo的模式,另一个名为foo_bar的模式,然后是foo中一个名为bar_none的表和一个名为none的foo_bar中的表。这会导致重复的触发器名称,因为我使用下划线作为分隔符。我跟CDC抱怨过这个问题但they closed the bug as won't fix。如果您碰巧使用带下划线的模式,请记住一些事项。

答案 1 :(得分:0)

您只能在SQL Server 2008 +中为DDL语句创建数据库触发器。

如果您需要在数据库中的每个表上使用DML触发器(INSTEAD OF INSERT),您将不得不自己管理它们或尝试创建数据库级别DDL触发器,负责在任何INSTEAD OF INSERTCREATE表语句中创建或更新ALTER触发器。可能会毛茸茸,几乎肯定会要求使用动态SQL。

出于好奇,您是否正在尝试建立某种审核机制?