使用动态SQL在数据库之间切换


8

我有一个涉及在多个数据库之间执行各种命令的过程-但是,当我使用动态SQL通过“ use @var”更改数据库时,它实际上并没有更改数据库。

在[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]作为命令而不是动态输入,则它将返回正确的名称。

有没有一种方法可以在数据库之间正确切换?

Answers:


9

当子流程结束时,在子流程(即EXEC/ sp_executesql)中进行的会话级更改将消失。这包括USESET语句以及在该子流程中创建的任何本地临时表。全局临时表的创建将在子流程中幸存,对子流程开始之前存在的本地临时表的修改以及对CONTEXT_INFO(我相信)的任何更改也将幸免。

因此,您不能动态更改当前数据库。如果需要执行类似的操作,则将需要在该动态SQL中执行所有依赖于新数据库上下文的后续语句。


12

当然,有办法-总是有办法...

如果声明变量并在其中存储数据库和要运行的过程,则可以使用参数执行它。

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%'

我知道这不会改变主查询中的数据库上下文,但想演示如何以安全的参数化方式方便地在另一个数据库中工作而不会造成太多麻烦。


0

基于@Magento先生的答案...

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;

我有很多与此相关的维护用途。


1
有一种更简单的方法。 exec OtherDatabase.sys.sp_executesql N'select db_name()'
David Browne-微软

由于您的评论更简洁,因此对您的评论进行了评论
Derreck Dean

@ DavidBrowne-Microsoft,这是Derreck在这里执行的操作,但是带有“ OtherDatabase”作为参数传递-是吗?因此,它们最终在“ proc”变量中以OtherDatabase.sys.sp_executesql而不是硬编码。
马古先生

好吧,他有另一个动态指定的数据库。如果您不需要它,那么直接拨打电话会更简单。
David Browne-微软

我一直在使用我的,因为我使用它来遍历一组特定的相关数据库,并使用已经烘焙到应用程序“主”数据库中的Ola Hallengren脚本对它们执行操作,例如索引,备份等。而不是实际的主数据库)。很高兴知道我可以像他的评论一样直接调出特定的数据库。
Derreck Dean

0

这也可以。

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

0

从上一篇文章中学到的知识使我更加深刻,给自己留下了深刻的印象。

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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.