複数のデータベース間でさまざまなコマンドを実行するプロセスがありますが、動的SQLを使用して 'use @var'でDBを変更しても、実際にはデータベースは変更されません。
これを[test_db]で実行:
declare @currentDB varchar(max)
declare @sql varchar(max)
set @currentDB = DB_NAME()
set @sql = 'use [' + @currentDB +']'
use master
exec(@sql)
select DB_NAME()
[Master]を現在のデータベース名として返します-use [test_db]
動的ではなくコマンドとして、正しい名前を返します。
データベースを正しく切り替えるこれを行う方法はありますか?
サブプロセスで行われたセッションレベルの変更(つまり、EXEC
/sp_executesql
)そのサブプロセスが終了したら終了します。これには、USE
およびSET
ステートメント、およびそのサブプロセスで作成されたlocal一時テーブルが含まれます。 global一時テーブルの作成はサブプロセスを存続します。したがって、サブプロセスの開始前に存在するローカル一時テーブルに加えられた変更、およびCONTEXT_INFO
(私は信じている)。
したがって、現在のデータベースを動的に変更することはできません。このようなことを行う必要がある場合は、その動的SQL内でも、新しいデータベースコンテキストに依存する後続のステートメントを実行する必要があります。
確かに、方法があります-常に方法があります...
変数を宣言し、それにデータベースと実行するプロシージャを格納すると、パラメーターを使用して実行できます。
例
use tempdb;
select db_name();
declare @db sysname = 'master.sys.sp_executesql';
exec @db N'select db_name()';
set @db = 'msdb.sys.sp_executesql';
exec @db N'select db_name()';
その後、任意のデータベースで実行されるパラメーターを使用してクエリを渡すことは簡単です
declare @proc sysname, @sql nvarchar(max), @params nvarchar(max);
select
@proc = 'ssc.sys.sp_executesql'
, @sql = N'select top 10 name from sys.tables where name like @table order by name;'
, @params = N'@table sysname';
exec @proc @sql, @params, @table = 'Tally%'
これはメインクエリのデータベースコンテキストを変更しないことは知っていますが、あまり気にせずに、安全なパラメーター化された方法で別のデータベースを簡単に操作する方法を示す必要がありました。
@Mister Magooの答えに基づいてこれを...
CREATE PROCEDURE dbo.Infrastructure_ExecuteSQL
(
@sql NVARCHAR(MAX),
@dbname NVARCHAR(MAX) = NULL
)
AS BEGIN
/*
PURPOSE
Runs SQL statements in this database or another database.
You can use parameters.
TEST
EXEC dbo.Infrastructure_ExecuteSQL 'SELECT @@version, db_name();', 'master';
REVISION HISTORY
20180803 DKD
Created
*/
/* For testing.
DECLARE @sql NVARCHAR(MAX) = 'SELECT @@version, db_name();';
DECLARE @dbname NVARCHAR(MAX) = 'msdb';
--*/
DECLARE @proc NVARCHAR(MAX) = 'sys.sp_executeSQL';
IF (@dbname IS NOT NULL) SET @proc = @dbname + '.' + @proc;
EXEC @proc @sql;
END;
これには、メンテナンス関連の用途がたくさんあります。
前の投稿から学んだことで、もう少し深く感動しました...
DECLARE @Debug BIT = 1
DECLARE @NameOfDb NVARCHAR(200) = DB_NAME()
DECLARE @tsql NVARCHAR(4000) = ''
IF OBJECT_ID('Tempdb.dbo.#tbl001') IS NOT NULL DROP TABLE #tbl001
CREATE TABLE #tbl001(
NameOfDb VARCHAR(111))
INSERT INTO #tbl001(NameOfDb)
VALUES('db1'),('db2'),('db3'),('db4')
SET @tsql = N'
DECLARE @sql nvarchar(max)
set @sql = N''
;WITH a AS (
SELECT NumOf = COUNT(*),
c.Field1,
c.Field2,
c.Field3
FROM ''+@NameOfDb2+''.dbo.TBLname c
WHERE Field3 = ''''TOP SECRET''''
GROUP BY
c.Field1,
c.Field2,
c.Field3
HAVING COUNT(*)>1
)
SELECT a.NumOf, c.*
FROM ''+@NameOfDb2+''.dbo.TBLname c
JOIN a ON c.Field1=a.Field1 AND c.Field2=a.Field2 AND c.Field3=a.Field3''
exec (@sql)
'
DECLARE SmplCrsr CURSOR STATIC LOCAL FORWARD_ONLY READ_ONLY FOR
SELECT * FROM #tbl001
OPEN SmplCrsr;
FETCH NEXT FROM SmplCrsr
INTO @NameOfDb
WHILE @@Fetch_Status=0
BEGIN
IF (@Debug = 1)
BEGIN
EXEC sys.sp_executesql @tsql,N'@NameOfDb2 varchar(111)',@NameOfDb
END
ELSE
BEGIN
PRINT @tsql + '-- DEBUG OFF'
END
FETCH NEXT FROM SmplCrsr
INTO @NameOfDb
END
CLOSE SmplCrsr;
DEALLOCATE SmplCrsr;
これも機能します。
declare @Sql nvarchar(max),@DatabaseName varchar(128)
set @DatabaseName = 'TestDB'
set @Sql = N'
declare @Sql nvarchar(max) = ''use ''+@DatabaseName
set @Sql = @Sql +''
select db_name()
''
exec (@Sql)
'
exec sp_executesql @Sql,N'@DatabaseName varchar(128)',@DatabaseName