DECLARE @DatabaseName NVARCHAR(max); SET @DatabaseName = 'MainDb'
USE @DatabaseName
Не получилось. Как это сделать?
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-инъекцией, для чего я указываю вам ссылку на ответ Барри.
Чтобы предотвратить SQL Injection, вы также можете использовать QUOTENAME(), она обертывает параметр в квадратных скобках:
DECLARE @DatabaseName sysname = 'MainDb'
, @SQL NVARCHAR(MAX);
SET @SQL = N'USE ' + QUOTENAME(@DatabaseName);
PRINT(@SQL);
-- USE [MainDb]
EXECUTE(@SQL);
Если вы используете script в SSMS, вы можете использовать режим SQLCMD (найденный в меню Query) до script переменная для имени вашей базы данных.
:setvar database "MainDb"
use $(database)
go
select * from sys.tables
Вместо динамического SQL, чтобы сделать эквивалент USE @Database
, могу ли я представить, что некоторый "предварительно обработанный динамический SQL" может быть еще лучшим решением для вас? Конечно, это зависит от того, зачем вам нужен динамический оператор USE. Я предполагаю, что вы действительно не можете с самого начала использовать правильную базу данных из своей строки подключения (что на самом деле является лучшим способом справиться с этим).
Динамический SQL, который я предлагаю, должен начинаться с создания синонима для каждого объекта, который вы хотите использовать:
CREATE SYNONYM dbo.CustomName FOR SomeDatabase.SomeSchema.SomeObject
Затем в хранимой процедуре или независимо от того, что вы хотели сделать после своего динамического оператора USE, просто обратитесь к dbo.CustomName
.
Чтобы легко переключаться, вы могли бы создать небольшую инфраструктуру. Создайте таблицу с псевдонимами синонимов и каким объектом они будут отображаться. Создайте хранимую процедуру, которая читает эту таблицу и запускает динамический SQL для обновления ваших SYNONYM.
Когда вам нужно переключиться, запустите этот SP, и он будет копировать все нужные вам объекты и повторно использовать их.
Эта стратегия не будет работать, если вам понадобятся различные процессы, одновременно получающие доступ к различным базам данных через синонимы. В этом случае вам лучше с другим методом.
Имейте в виду, что вы все равно можете избежать динамического SQL с помощью некоторых умных способов. Например, возможно, вместо того, чтобы помещать SP, который вы хотите запустить в основной базе данных, и динамически выполнять его манипуляции в каждой подзадачи, поместить SP в каждую подбазу и затем вызвать каждый SP.
EXEC('USE ' + @DatabaseName + ';SELECT --etc')
Пока вы доверяете @DatabaseName
, чтобы не содержать ;DROP DATABASE MyDB
:)
Для достижения этой цели вам придется использовать Dynamic SQL.
Прежде чем приступать к изучению динамического SQL, я предлагаю вам прочитать эту замечательную статью http://www.sommarskog.se/dynamic_sql.html
Если вам нужно сделать это как часть процесса развертывания или какой-то бэкэнд-процесс, в отличие от того, что пользователь начал использовать, как альтернатива поместить все в динамические выражения, такие как
EXECUTE('USE ' + @DatabaseName + ';select * from INFORMATION_SCHEMA.TABLES;
select * from INFORMATION_SCHEMA.COLUMNS;
select * from INFORMATION_SCHEMA.ROUTINES;
select * from INFORMATION_SCHEMA.PARAMETERS;')
вы можете пойти в Old School, используя утилиту SQLCMD и используя вашу любимую программу сценариев. Я бы порекомендовал 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;
Вы можете выполнить этот input.sql для нескольких dbs, поставив следующий пакетный файл C:\Test.bat(этот пакет предполагается в том же каталоге, что и 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
Преимущества этого подхода:
Я нашел сочетание SYNONYM и exec dynamic SQL, единственное, что я получил, чтобы работать (esp как часть хранимой процедуры с несколькими базами данных). Упрощенная версия того, что сработало для меня, чтобы запросить базу данных из имени переменной.
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 ['[email protected]_DB+'].[dbo].[TableA]'
exec (@SQL_SCRIPT)
DROP SYNONYM dbo.To_TableB
SET @SQL_SCRIPT = 'CREATE SYNONYM dbo.To_TableB FOR ['[email protected]_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