DECLARE @DatabaseName NVARCHAR(max); SET @DatabaseName = 'MainDb'
USE @DatabaseName
不行。如何制作?
答案 0 :(得分:25)
如果要像这样动态地执行动态SQL,则必须使用动态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);
答案 1 :(得分:15)
如果您在SSMS中运行脚本,可以使用SQLCMD Mode(在“查询”菜单下找到)为您的数据库名称编写变量脚本。
:setvar database "MainDb"
use $(database)
go
select * from sys.tables
答案 2 :(得分:6)
我可以提交一些“预处理的动态SQL”来为您提供更好的解决方案,而不是使用动态SQL来执行USE @Database
的等效操作吗?当然,这取决于您需要动态USE语句的原因。我假设您真的不能从一开始就使用连接字符串中的正确数据库(这是处理此问题的最佳方法)。
我建议的动态SQL首先要为你想要引用的每个对象创建一个同义词:
CREATE SYNONYM dbo.CustomName FOR SomeDatabase.SomeSchema.SomeObject
然后在存储过程中或在动态USE语句之后想要做的任何事情,只需参考dbo.CustomName
。
要轻松切换,您可以创建一个基础架构。使用同义词别名和它们将映射到的对象构建一个表。创建一个读取此表的存储过程并运行动态SQL以更新SYNONYM。
当您需要切换时,运行该SP,它将翻录您需要的所有对象并重新链接它们。
如果您需要通过同义词同时访问不同数据库的各种进程,则此策略将不起作用。在这种情况下,你最好用其他方法。
请记住,您仍然可以通过某些聪明的方式避免使用动态SQL。例如,可能不是将要在主数据库中运行的SP放入并使其在每个子数据库上动态执行操作,而是将SP放在每个子数据库中,然后调用每个SP。
答案 3 :(得分:2)
EXEC('USE ' + @DatabaseName + ';SELECT --etc')
只要您信任@DatabaseName
不包含;DROP DATABASE MyDB
:)
答案 4 :(得分:2)
如果您需要在部署过程或某些后端流程中执行此操作,而不是由用户启动,则可以选择将所有内容放入动态语句中
EXECUTE('USE ' + @DatabaseName + ';select * from INFORMATION_SCHEMA.TABLES;
select * from INFORMATION_SCHEMA.COLUMNS;
select * from INFORMATION_SCHEMA.ROUTINES;
select * from INFORMATION_SCHEMA.PARAMETERS;')
您可以使用SQLCMD实用程序并使用您喜欢的脚本程序去Old School。我会推荐PowerShell,但对于这个示例,我将使用经典的DOS批次。
假设你有一个看起来像这样的文件C:\ input.sql
select * from INFORMATION_SCHEMA.TABLES;
select * from INFORMATION_SCHEMA.COLUMNS;
select * from INFORMATION_SCHEMA.ROUTINES;
select * from INFORMATION_SCHEMA.PARAMETERS;
您可以通过将以下批处理文件C:\ Test.bat放在多个dbs上执行该input.sql(此批处理假定与input.sql位于同一目录中)
C:\ TEST.BAT
set var=maindb
"C:\Program Files\Microsoft SQL Server\100\Tools\Binn\SQLCMD.EXE" -d %var% -i"input.sql"
set var=master
"C:\Program Files\Microsoft SQL Server\100\Tools\Binn\SQLCMD.EXE" -d %var% -i"input.sql"
然后你可以通过
执行它C:\> TEST.BAT
这种方法的优点是
答案 5 :(得分:1)
您必须使用动态SQL来实现此目的。
在开始探索Dynamic SQL之前,我建议您阅读这篇优秀的文章http://www.sommarskog.se/dynamic_sql.html
答案 6 :(得分:1)
我发现SYNONYM和exec动态SQL的组合是我唯一能够工作的东西(特别是作为具有多个数据库的存储过程的一部分)。我可以从变量名查询数据库的简化版本。
CREATE PROCEDURE DM_TCM_TO_COMCARE
@FROM_DB varchar(100) = '',
@TO_DB varchar(100) = ''
AS
BEGIN
--CHECK INPUT VARIABLES
DROP SYNONYM dbo.From_TableA
SET @SQL_SCRIPT = 'CREATE SYNONYM dbo.From_TableA FOR ['+@FROM_DB+'].[dbo].[TableA]'
exec (@SQL_SCRIPT)
DROP SYNONYM dbo.To_TableB
SET @SQL_SCRIPT = 'CREATE SYNONYM dbo.To_TableB FOR ['+@TO_DB+'].[dbo].[TableB]'
exec (@SQL_SCRIPT)
select * from dbo.From_TableA
select * from dbo.To_TableB
insert into dbo.To_TableB
select * from dbo.From_TableA where 1 = 1
-- etc
END
GO