Chuyển đổi giữa các cơ sở dữ liệu với SQL động


8

Tôi có một quy trình bao gồm thực thi các lệnh khác nhau giữa nhiều cơ sở dữ liệu - tuy nhiên, khi tôi sử dụng SQL động để thay đổi DB bằng 'use @var', thì nó không thực sự thay đổi cơ sở dữ liệu.

Thực hiện điều này trong [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()

Trả về [Master] làm tên cơ sở dữ liệu hiện tại - nếu tôi đặt use [test_db]dưới dạng lệnh, thay vì động, thì nó trả về tên chính xác.

Có cách nào để làm điều này sẽ chuyển đổi chính xác giữa các cơ sở dữ liệu?

Câu trả lời:


9

Các thay đổi cấp phiên được thực hiện trong quy trình phụ (tức là EXEC/ sp_executesql) sẽ biến mất khi quy trình phụ đó kết thúc. Điều này bao gồm USESETbáo cáo cũng như bất kỳ bảng tạm thời cục bộ nào được tạo trong quy trình phụ đó. Việc tạo các bảng tạm thời toàn cầu sẽ tồn tại trong quy trình phụ và do đó, các sửa đổi sẽ được thực hiện đối với các bảng tạm thời cục bộ tồn tại trước khi bắt đầu quy trình phụ và bất kỳ thay đổi nào đối với CONTEXT_INFO(tôi tin).

Vì vậy, không, bạn không thể tự động thay đổi cơ sở dữ liệu hiện tại. Nếu bạn cần làm một cái gì đó như thế này, bạn sẽ cần phải thực hiện bất kỳ câu lệnh tiếp theo nào dựa trên bối cảnh cơ sở dữ liệu mới trong SQL động đó.


12

Chắc chắn, có một cách - luôn có một cách ...

Nếu bạn khai báo biến và lưu trữ trong đó cơ sở dữ liệu và thủ tục để chạy, bạn có thể thực hiện nó, với các tham số.

Thí dụ

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()';

Sau đó, việc truyền một truy vấn với các tham số sẽ được chạy trong bất kỳ cơ sở dữ liệu nào là chuyện nhỏ

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

Tôi biết điều này không thay đổi bối cảnh cơ sở dữ liệu trong truy vấn chính, nhưng muốn chứng minh làm thế nào bạn có thể làm việc thuận tiện trong cơ sở dữ liệu khác theo cách tham số hóa an toàn mà không phải bận tâm quá nhiều.


0

Dựa trên câu trả lời của @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;

Tôi có rất nhiều sử dụng liên quan đến bảo trì cho việc này.


1
Có một cách thậm chí còn dễ dàng hơn. exec OtherDatabase.sys.sp_executesql N'select db_name()'
David Browne - Microsoft

Nâng cao nhận xét của bạn vì bạn thậm chí còn ngắn gọn hơn
Derreck Dean

@ DavidBrowne-Microsoft đó là những gì Derreck đang làm ở đây, nhưng với "OtherDatabase" được truyền dưới dạng tham số - phải không? Vì vậy, họ kết thúc với OtherDatabase.sys.sp_executesql trong biến "Proc" thay vì mã hóa cứng.
Magoo

Chà, anh ấy có cơ sở dữ liệu khác được chỉ định động. Nếu bạn không cần điều đó, thì đơn giản hơn là chỉ cần gọi trực tiếp.
David Browne - Microsoft

Tôi vẫn đang sử dụng của tôi vì tôi sử dụng điều này để lặp qua một bộ cơ sở dữ liệu liên quan cụ thể và thực hiện các hành động trên chúng như lập chỉ mục, sao lưu, v.v. bằng cách sử dụng tập lệnh Ola Hallengren mà tôi đã đưa vào cơ sở dữ liệu 'chính chủ' của ứng dụng của mình ( không phải là chủ db thực tế). Thật tốt khi biết rằng tôi có thể gọi trực tiếp đến một cơ sở dữ liệu cụ thể như trong nhận xét của anh ấy.
Derreck Trưởng

0

Điều này làm việc quá.

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

Học hỏi từ bài trước tôi đã đi sâu hơn một chút và tự gây ấn tượng ...

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.