动态 SQL - 在 SET SQL 字符串中使用声明的 VARCHAR

Dynamic SQL - Use declared VARCHAR in SET SQL string

如何在SQL字符串中使用声明的变量@CodeID?当我 运行 以下语句时,我收到“无效的对象名称 (..)”错误。

WHILE @FolderID <= @FolderMaxID
BEGIN
    SELECT @Db  = Db 
    FROM #Folders 
    WHERE ID =  @FolderID

    SET @Sql = N'

        DECLARE @CodeID NVARCHAR(256)

        SELECT TOP(1)  @CodeID=CodeType
        FROM ' + @Db + '.bla.Field 
        WHERE Name= ''Example''

        SELECT DISTINCT C.Name
        FROM ' + @Db + '.Document 
            INNER JOIN ' + @Db + '.bla.Code_@CodeID  C  ON D.ID = C.ID'
        
    EXEC ( @Sql )
    SET @FolderID = @FolderID + 1
END

在我看来,您需要两层动态 SQL,第一层插入数据库名称(来自#folders),第二层插入构造的 table 名称(基于 database-local bla.Field table).

的 CodeType 列

我不知道有什么方法可以使用 sp_executesql 参数化数据库名称或 table 名称,所以我坚持使用 build-up 动态 SQL 和 EXEC ( ). (如果有人证明在不使用参数时更喜欢 sp_executesql 而不是 EXEC,那么可能值得切换。)

试试这样的东西:

WHILE @FolderID <= @FolderMaxID
BEGIN
    SELECT @Db  = Db 
    FROM #Folders 
    WHERE ID =  @FolderID

    SET @Sql = N'

        DECLARE @CodeID NVARCHAR(256)

        SELECT TOP(1)  @CodeID=CodeType
        FROM ' + QUOTENAME(@Db) + '.bla.Field 
        WHERE Name= ''Example''

        DECLARE @Sql2 NVARCHAR(MAX) = N''
            SELECT DISTINCT C.Name
            FROM ' + QUOTENAME(@Db) + '.bla.Document D
                INNER JOIN ' + QUOTENAME(@Db) + '.bla.'' + QUOTENAME(''Code_'' + @CodeID) + '' C  ON D.ID = C.ID
        ''
        EXEC @sql2
        '
    
    EXEC ( @Sql )
    SET @FolderID = @FolderID + 1
END

这在动态 SQL 中实现了动态 SQL。外部 sql 模板中的双引号在内部 sql 中变为单引号。最初发布的代码似乎缺少文档 table 的架构限定符和别名,因此我插入了它们(“bla”和“D”)。我还按照 Larnu 的建议在注入的名称周围添加了 QUOTENAME。

第一级动态 sql 会生成如下内容:

SELECT TOP(1)  @CodeID=CodeType
FROM [db1].bla.Field 
WHERE Name= 'Example'

DECLARE @Sql2 NVARCHAR(MAX) = N'
    SELECT DISTINCT C.Name
    FROM [db1].bla.Document D
        INNER JOIN [db1].bla.' + QUOTENAME('Code_' + @CodeID) + ' C  ON D.ID = C.ID
'
EXEC @sql2

第二层会生成如下内容:

SELECT DISTINCT C.Name
FROM [db1].bla.Document D
    INNER JOIN [db1].bla.[Code_Table1] C  ON D.ID = C.ID

请注意,每次循环迭代都会生成一个单独的结果。如果您希望合并结果,您将需要定义一个#temp table,将单独的结果插入到 table 中,然后 select 脚本末尾的合并结果。

请注意,我还没有测试上面的具体代码,因此如果不能直接运行,可能需要进行一些调试(在 EXEC 之前添加“PRINT @sql2”)。

附录

根据下面的@trenton-ftw 评论,out 参数可用于捕获第一个查询的结果,以便它可以包含在第二个查询中而无需嵌套。仍然需要执行两次死刑。下面是修改后的例子。


DECLARE @Folders TABLE (ID INT IDENTITY(1,1), Db sysname)
INSERT @Folders VALUES ('db1'), ('db2')

DECLARE @SearchName NVARCHAR(256) = 'Example' 
DECLARE @Db sysname
DECLARE @Sql NVARCHAR(MAX)
DECLARE @CodeID NVARCHAR(256)

DECLARE @FolderMaxID INT = (SELECT MAX(ID) FROM @Folders)
DECLARE @FolderID INT = 1
WHILE @FolderID <= @FolderMaxID
BEGIN
    SELECT @Db = Db 
    FROM @Folders 
    WHERE ID = @FolderID

    SET @Sql = N'
        SET @CodeID = @SearchName + ''-Test''
        --SELECT TOP(1) @CodeID = CodeType
        --FROM ' + QUOTENAME(@Db) + '.bla.Field 
        --WHERE Name = @SearchName'
    PRINT @Sql
    EXEC sp_executesql @Sql,
        N'@SearchName NVARCHAR(256), @CodeID NVARCHAR(256) OUTPUT',
        @SearchName, @CodeID OUTPUT

    SET @Sql = N'
        --SELECT DISTINCT C.Name
        --FROM ' + QUOTENAME(@Db) + '.bla.Document D
        --    INNER JOIN ' + QUOTENAME(@Db) + '.bla.' + QUOTENAME('Code_' + @CodeID) + ' C  ON D.ID = C.ID'
    PRINT @Sql
    EXEC sp_executesql @sql

    SET @FolderID = @FolderID + 1
END

出于演示目的,我还将搜索名称参数化为输入参数,并添加了一些临时代码以使其成为 stand-alone testable。最终版本将取消对实际 sql 的注释,并删除打印语句和测试 @CodeID 赋值。