动态 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 赋值。
如何在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 赋值。