是否可以使用带参数的sp_executesql发出CREATE语句?

bdu*_*kes 13 sql-server dynamic-sql sp-executesql

我正在尝试动态创建触发器,但在使用sp_executesql参数并将参数传递到动态SQL时遇到了一个令人困惑的问题.以下简单测试用例有效:

DECLARE @tableName sysname = 'MyTable';
DECLARE @sql nvarchar(max) = N'
    CREATE TRIGGER TR_' + @tableName + N' ON ' + @tableName + N' FOR INSERT
        AS
        BEGIN
            PRINT 1
        END';
EXEC sp_executesql @sql
Run Code Online (Sandbox Code Playgroud)

但是,我希望能够@tableName在脚本中使用(和其他值)作为变量,因此我将其传递给sp_executesql调用:

DECLARE @tableName sysname = 'ContentItems';
DECLARE @sql nvarchar(max) = N'
    CREATE TRIGGER TR_' + @tableName + N' ON ' + @tableName + N' FOR INSERT
        AS
        BEGIN
            PRINT @tableName
        END';
EXEC sp_executesql @sql, N'@tableName sysname', @tableName=@tableName
Run Code Online (Sandbox Code Playgroud)

运行上面的代码时,我收到一个错误:

Msg 156,Level 15,State 1,Line 2
关键字'TRIGGER'附近的语法不正确.

在尝试了几件事之后,我发现即使我根本不使用@tableName动态SQL,我仍然会遇到这个错误.我也试图创建一个错误PROCEDURE(显然,除了关键字'PROCEDURE'附近的消息是不正确的语法.)

由于SQL直接或不提供参数时运行正常sp_executesql,这似乎我在SQL引擎中遇到了一个真正的限制,但我没有看到它记录在任何地方.有没有人知道是否有办法接受动态CREATE脚本,或至少了解正在遇到的潜在限制?

更新 我可以添加一个PRINT语句,并获得以下有效的SQL,并成功运行(直接运行时).如果SQL中没有任何动态(它只是一个没有连接的单个字符串),我仍然会收到错误.

CREATE TRIGGER TR_ContentItems ON ContentItems FOR INSERT
    AS
    BEGIN
        PRINT @tableName
    END
Run Code Online (Sandbox Code Playgroud)

无论是使用sysname还是nvarchar(max)参数,我也会得到相同的错误.

Sha*_*awn 2

我强烈警告不要将动态 SQL 与表名一起使用。您正在为自己设置一些严重的 SQL 注入问题。您应该验证进入的任何内容@tableName

也就是说,在你的例子中......

DECLARE @tableName sysname = 'ContentItems';
DECLARE @sql nvarchar(max) = N'
    CREATE TRIGGER TR_' + @tableName + N' ON ' + @tableName + N' FOR INSERT
        AS
        BEGIN
            PRINT @tableName
        END';
EXEC sp_executesql @sql, N'@tableName sysname', @tableName=@tableName
Run Code Online (Sandbox Code Playgroud)

...您试图将您声明的内容输入@tableName到您正在为其创建的文本中@sql,然后您尝试通过 传递参数spexecutesql。这使得你@sql在尝试调用它时无效。

你可以试试:

DECLARE @tableName sysname = 'ContentItems';
DECLARE @sql nvarchar(max) = N'
    CREATE TRIGGER TR_'' + @tableName + N'' ON '' + @tableName + N'' FOR INSERT
        AS
        BEGIN
            PRINT @tableName
        END';
EXEC sp_executesql @sql, N'@tableName sysname', @tableName=@tableName
Run Code Online (Sandbox Code Playgroud)

...这会给你字符串...

'
CREATE TRIGGER TR_' + @tableName + N' ON ' + @tableName + N' FOR INSERT
    AS
    BEGIN
        PRINT @tableName
    END'
Run Code Online (Sandbox Code Playgroud)

...然后它可以接受您传递的参数...

EXEC sp_executesql @sql, N'@tableName sysname', @tableName=@tableName ;
Run Code Online (Sandbox Code Playgroud)

同样,在将任何内容传递到将使用动态表名称的动态 SQL 之前,我会使用一些严格的验证(和白名单)。

注意:如下所述,我相信您可以使用 执行的 DML 语句受到限制sp_executesql(),并且我认为参数化也受到限制。根据您的其他评论,听起来您并不真正需要一个动态过程,而是一种对少数元素重复特定任务的方法。如果是这种情况,我的建议是通过复制/粘贴手动执行此操作,然后执行语句。