我该怎么做:USE @databaseName

iLe*_*ing 23 t-sql sql-server

 DECLARE @DatabaseName NVARCHAR(max); SET @DatabaseName = 'MainDb'
 USE @DatabaseName
Run Code Online (Sandbox Code Playgroud)

不行.怎么做?

Ada*_*Dev 27

如果要像这样动态地执行它,则必须使用动态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
Run Code Online (Sandbox Code Playgroud)

所以你需要这样做:

DECLARE @DatabaseName NVARCHAR(MAX)
SET @DatabaseName = 'MainDb'
EXECUTE('USE ' + @DatabaseName + ';SELECT name FROM sys.tables') -- SQL injection risk!
Run Code Online (Sandbox Code Playgroud)

当然,你需要非常小心SQL注入,为此我指出你在Barry的答案中的链接.

要防止SQL注入,您还可以使用QUOTENAME()函数,它将参数包装在方括号中:

DECLARE @DatabaseName sysname = 'MainDb'
    , @SQL NVARCHAR(MAX);

SET @SQL = N'USE ' + QUOTENAME(@DatabaseName);

PRINT(@SQL);
-- USE [MainDb]

EXECUTE(@SQL);
Run Code Online (Sandbox Code Playgroud)

  • 与大多数事情一样,它都是关于正确工作的背景,利弊和正确方法.在某些多租户解决方案中,您将拥有完全相同的多个数据库,并且动态/自动配置它们,您将无法在设计时知道数据库名称.一天结束时,*可以*表示问题/嗅觉,但与大多数事情一样,"它取决于" (4认同)

Joe*_*lli 15

如果您在SSMS中运行脚本,则可以使用SQLCMD模式(在"查询"菜单下找到)为您的数据库名称编写变量脚本.

:setvar database "MainDb"
use $(database)
go

select * from sys.tables
Run Code Online (Sandbox Code Playgroud)


Eri*_*ikE 6

请改用同义词

而不是动态SQL做相同的USE @Database,我可以提交一些"预处理动态SQL"可能是一个更好的解决方案吗?当然,这取决于您需要动态USE语句的原因.我假设你真的不能从一开始就使用连接字符串中的正确数据库(这是处理这个问题的最佳方法).

我建议的动态SQL是通过为要引用的每个对象创建一个同义词来开始:

CREATE SYNONYM dbo.CustomName FOR SomeDatabase.SomeSchema.SomeObject
Run Code Online (Sandbox Code Playgroud)

然后在存储过程中或者在动态USE语句之后想要做的任何事情,请参阅dbo.CustomName.

要轻松切换,您可以创建一个基础架构.使用同义词别名和它们将映射到的对象构建一个表.创建一个读取此表的存储过程并运行动态SQL以更新SYNONYM.

当您需要切换时,运行该SP,它将翻录您需要的所有对象并重新链接它们.

警告

如果您需要通过同义词同时访问不同数据库的各种进程,则此策略将不起作用.在这种情况下,你最好用其他方法.

请记住,您仍然可以通过一些聪明的方式避免使用动态SQL.例如,可能不是将要运行的SP放在主数据库中,而是让它动态地对每个子数据库执行操作,而是将SP放在每个子数据库中,然后调用每个SP.