触发器 - 在动态 SQL 中使用插入/删除的表

Joh*_*hnG 7 trigger sql-server dynamic-sql sql-clr

在触发器中,我试图创建一个唯一的表名(使用NEWID()),我可以存储在插入和删除的表中找到的数据。

Declare @NewID varchar(50) = Replace(convert(Varchar(50),NEWID()),'-','')
Declare @SQLStr varchar(8000)
Set @SQLStr= 'Select * into [TMPIns' + @newID + '] from inserted'
Exec (@SQLStr)
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:无效的对象名称“插入”

我知道我可以做到:

Select * into #inserted from inserted
Set @SQLStr= 'Select * into [TMPIns' + @newID + '] from #inserted'
Exec (@SQLStr)
Run Code Online (Sandbox Code Playgroud)

但是我不想使用 TempDB,因为这些表会变得很大,而且我也觉得它是多余的。有没有办法避免创建#inserted?

Sol*_*zky 2

如果没有更多地了解此请求的预期目标,那么即使解决了这个迫在眉睫的问题,工作代码也可能无法提供任何真正有用的东西。一些担忧是:

  • 根据此触发器将放置在多少个表上和/或 DML 操作的频率,这可能会导致此触发器创建表的数据库中出现轻微的性能问题,因为创建表需要架构锁(I相信)并且过于频繁地这样做可能会使其他一些操作变得复杂。
  • 如果这个触发器将被放置在多个表上,您将如何区分不同表之间的操作(除非为动态创建的表提供自己的前缀)?
  • UpdatedDate表中是否有一个或某个日期字段?如果不是,那么不查看表创建日期就没有时间顺序的意义。
  • 您打算如何清理所有这些不同的桌子?也许最好创建一个架构来保存这些表?
  • 您是否计划在任何地方指示发生了 DML 操作?
  • 如果您想跟踪 上的“之前”和“之后”值UPDATE,那么您需要捕获inserteddeleted表。但是,如果它们具有基于 GUID 的名称,那么您将无法在特定 UPDATE 操作的“插入”和“删除”复制表之间进行关联。您必须重复使用相同的 GUID 值并在表名称前缀中表示“插入”或“删除”。如果您没有动态创建表,那么您可以包含指定 DML 操作的列,将 和inserted表转储deleted到现有表中,然后仅使用序列中的 GUID 或 INT 来关联同一UPDATE操作的 2 行。
  • 根据您使用的 SQL Server 版本和版本,您可能需要研究更改跟踪更改数据捕获

然而,尽管如此,通过动态 SQL 与inserteddeleted表交互的问题是一个有趣的问题。不幸的是,它不能在 T-SQL 中完成。所以现在这也是一个挑战:-)。幸运的是,这实际上是可以做到的。为何如此?在我们的朋友 SQLCLR 先生的帮助下。

现在,似乎没有很多情况真正需要 SQLCLR 触发器,甚至可以从 SQLCLR 触发器中受益。它们似乎是使用 SQLCLR 可以创建的最无用的东西。然而,我们这里有一个他们非常适合的场景。从 SQLCLR 代码提交的 SQL 是动态 SQL。并且 SQLCLR 触发器可以访问inserteddeleted表,因此看起来 SQLCLR 触发器可以访问动态 SQL 中的inserted和表。deleted下面是完成此请求的代码(请注意,数据库连接正在使用进程内“上下文连接”,因此可以用 标记程序集PERMISSION_SET = SAFE;无需非对称密钥或将数据库设置为TRUSTWORTHY ON):

要创建触发器的测试表(如果使用 Visual Studio / SSDT,表定义必须包含在项目中):

CREATE TABLE TableThatHasTriggers
(
   TableThatHasTriggersID INT IDENTITY(1, 1) NOT NULL
                          CONSTRAINT [PK_TableThatHasTriggers] PRIMARY KEY, 
   InsertTime DATETIME NOT NULL
              CONSTRAINT [DF_TableThatHasTriggers_InsertTime] DEFAULT (GETDATE()),
   SomeValue NVARCHAR(50) COLLATE Latin1_General_100_CI_AS NULL
);
Run Code Online (Sandbox Code Playgroud)

SQLCLR C# 代码:

using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;

public class Triggers
{
    [SqlTrigger(Target = "TableThatHasTriggers", Event = "FOR INSERT, UPDATE")]
    public static void tr_TableThatHasTriggers_audit()
    {
        string _AuditSQL = @"
                SELECT ins.*
                INTO   dbo.[TMPIns_" + Guid.NewGuid().ToString().Replace("-", "") + @"]
                FROM   INSERTED ins;
";

        SqlConnection _Connection = new SqlConnection("Context Connection = true");
        SqlCommand _Command = _Connection.CreateCommand();
        _Command.CommandText = _AuditSQL;

        // SqlContext.Pipe.Send(_AuditSQL); // display query for debugging purposes ONLY

        try
        {
            _Connection.Open();
            _Command.ExecuteNonQuery();
        }
        finally
        {
            _Command.Dispose();
            _Connection.Dispose();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将 SQLCLR 触发器放置到表上的 T-SQL 包装器对象:

using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;

public class Triggers
{
    [SqlTrigger(Target = "TableThatHasTriggers", Event = "FOR INSERT, UPDATE")]
    public static void tr_TableThatHasTriggers_audit()
    {
        string _AuditSQL = @"
                SELECT ins.*
                INTO   dbo.[TMPIns_" + Guid.NewGuid().ToString().Replace("-", "") + @"]
                FROM   INSERTED ins;
";

        SqlConnection _Connection = new SqlConnection("Context Connection = true");
        SqlCommand _Command = _Connection.CreateCommand();
        _Command.CommandText = _AuditSQL;

        // SqlContext.Pipe.Send(_AuditSQL); // display query for debugging purposes ONLY

        try
        {
            _Connection.Open();
            _Command.ExecuteNonQuery();
        }
        finally
        {
            _Command.Dispose();
            _Connection.Dispose();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)