DECLARE @DatabaseName NVARCHAR(max); SET @DatabaseName = 'MainDb'
USE @DatabaseName
行不通。怎么做呢?
如果你想像那样动态地执行它,你必须使用动态 SQL。意味着您想要在该数据库上下文中执行的任何内容,您也需要包含在动态 SQL 语句中。
即假设您想列出 MainDB 中的所有表:
这不起作用,因为 USE 语句位于不同的上下文中 - 一旦 EXECUTE 运行,以下 SELECT 将不会在同一上下文中运行,因此不会在 MainDb 中运行(除非已设置连接)到MainDb)
DECLARE @DatabaseName NVARCHAR(MAX)
SET @DatabaseName = 'MainDb'
EXECUTE('USE ' + @DatabaseName) -- SQL injection risk!
SELECT name FROM sys.tables
所以你需要这样做:
DECLARE @DatabaseName NVARCHAR(MAX)
SET @DatabaseName = 'MainDb'
EXECUTE('USE ' + @DatabaseName + ';SELECT name FROM sys.tables') -- SQL injection risk!
当然,你需要非常小心 SQL 注入,为此我向你指出 Barry 答案中的链接。
为了防止 SQL 注入,您还可以使用 QUOTENAME() 函数,它将参数括在方括号中:
DECLARE @DatabaseName sysname = 'MainDb'
, @SQL NVARCHAR(MAX);
SET @SQL = N'USE ' + QUOTENAME(@DatabaseName);
PRINT(@SQL);
-- USE [MainDb]
EXECUTE(@SQL);
如果您在 SSMS 中运行脚本,则可以使用 SQLCMD 模式(位于“查询”菜单下)为您的数据库名称编写变量脚本。
:setvar database "MainDb"
use $(database)
go
select * from sys.tables
我可以提出一些“预处理的动态 SQL”对您来说可能是更好的解决方案吗?当然,这取决于你为什么需要动态USE语句。我假设您确实无法从一开始就使用连接字符串中的正确数据库(这确实是处理此问题的最佳方法)。
我建议的动态 SQL 是首先为要引用的每个对象创建一个同义词:
USE @Database
然后在存储过程或动态 USE 语句之后您想要执行的任何操作中,只需参考
CREATE SYNONYM dbo.CustomName FOR SomeDatabase.SomeSchema.SomeObject
。
为了轻松地进行切换,您可以创建一些基础设施。使用同义词别名以及它们将映射到的对象构建一个表。创建一个存储过程来读取此表并运行动态 SQL 来更新您的同义词。
当您需要切换时,运行该 SP,它将删除您需要的所有对象并重新链接它们。
注意事项
请记住,您仍然可以通过一些巧妙的方式避免动态 SQL。例如,也许不是将要运行的 SP 放在主数据库中并让它动态地在每个子数据库上执行操作,而是将 SP 放入每个子数据库中,然后调用每个 SP。
dbo.CustomName
您可以通过使用SQLCMD
实用程序并使用您最喜欢的脚本程序来走老路。我推荐使用 PowerShell,但对于此示例,我将使用经典的 DOS 批处理。 假设您有如下所示的文件 C:\input.sql
EXECUTE('USE ' + @DatabaseName + ';select * from INFORMATION_SCHEMA.TABLES;
select * from INFORMATION_SCHEMA.COLUMNS;
select * from INFORMATION_SCHEMA.ROUTINES;
select * from INFORMATION_SCHEMA.PARAMETERS;')
您可以通过将以下批处理文件放入 C:\Test.bat 来在多个数据库上执行该 input.sql(该批处理假定与 input.sql 位于同一目录中)
C:\测试.bat
select * from INFORMATION_SCHEMA.TABLES;
select * from INFORMATION_SCHEMA.COLUMNS;
select * from INFORMATION_SCHEMA.ROUTINES;
select * from INFORMATION_SCHEMA.PARAMETERS;
然后你可以通过执行它
C:\>测试.bat
这种方法的优点是
您可以像您一样开发您的Input.SQL 通常会
只要你相信
EXEC('USE ' + @DatabaseName + ';SELECT --etc')
不会包含
@DatabaseName
:);DROP DATABASE MyDB
SET @db = 'db_name';
SET @sql = 'USE' + QUOTENAME(@db);
执行 sp_executesql @sql;