我该怎么做:USE @databaseName

时间:2010-09-24 15:30:25

标签: sql-server tsql

 DECLARE @DatabaseName NVARCHAR(max); SET @DatabaseName = 'MainDb'
 USE @DatabaseName

不行。如何制作?

7 个答案:

答案 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

这种方法的优点是

  • 你可以像你一样开发Input.SQL 通常会
  • 它也没有EXECUTE具有的Varchar限制..
  • 脚本PowerShell,VBS,MS DOS Batch,Shell Execute
  • 的许多选项
  • 许多SQLCMd选项(输出文件,超时等)

答案 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