尝试使用游标循环更改数据库,但数据库不会更改

Attempting to change databases using cursor loop, but database won't change

我正在编写一个脚本,当执行时,如果 table 命名为 CleanUpTable 列表。所有数据库都驻留在同一台服务器上。我正在使用 SQL Server 2014。

我试图通过创建一个循环遍历数据库名称列表的外部游标循环和一个在给定数据库中拉入 table 名称列表的内部游标循环来做到这一点在 CleanUpTable 中列出并删除那些 table。但是,似乎外循环无法更改数据库。该脚本只会访问起始数据库的相关 table X 次,X 是外部游标中有多少数据库名称条目。因此,例如,如果我从 Database1 开始,并且我的外部光标中有三个数据库名称条目,而不是得到:

DROP TABLE Database1..TableB
DROP TABLE Database1..TableC
DROP TABLE Database2..TableE
DROP TABLE Database2..TableF
DROP TABLE Database3..TableH
DROP TABLE Database3..TableI

我得到:

DROP TABLE Database1..TableB
DROP TABLE Database1..TableC
DROP TABLE Database1..TableB
DROP TABLE Database1..TableC
DROP TABLE Database1..TableB
DROP TABLE Database1..TableC

...这不是我真正想要的,所以我假设外循环有问题。我知道通常的数据库更改命令是

USE Database1;
GO

但我无法弄清楚如何使用 EXEC() 执行此操作。它一直告诉我 GO 附近有一个语法错误,我猜是因为 GO 不能和 'USE Database1;' 在同一行,而且我不知道在使用 EXEC() 时如何换行。我尝试使用

SET @ChangeDB = 'USE ' + @DatabaseName + ';'
EXEC(@ChangeDB + CHAR(13) + 'GO') 

SET @ChangeDB ='USE ' + @DatabaseName  + ';' +CHAR(13) + 'GO'
EXEC(@ChangeDB)

但这些也返回语法错误。

相关代码如下:

DB/Table 创建脚本:

CREATE DATABASE Database1;
CREATE DATABASE Database2;
CREATE DATABASE Database3;
CREATE DATABASE Database4;

CREATE TABLE Database1.dbo.TableA (Column1 INT, Column2 INT);
CREATE TABLE Database1.dbo.TableB (Column1 INT, Column2 INT);
CREATE TABLE Database1.dbo.TableC (Column1 INT, Column2 INT);

CREATE TABLE Database2.dbo.TableD (Column1 INT, Column2 INT);
CREATE TABLE Database2.dbo.TableE (Column1 INT, Column2 INT);
CREATE TABLE Database2.dbo.TableF (Column1 INT, Column2 INT);

CREATE TABLE Database3.dbo.TableG (Column1 INT, Column2 INT);
CREATE TABLE Database3.dbo.TableH (Column1 INT, Column2 INT);
CREATE TABLE Database3.dbo.TableI (Column1 INT, Column2 INT);

CREATE TABLE Database4.dbo.CleanUpTableList (DBName VARCHAR(20), TableName VARCHAR(20));

INSERT INTO Database4..CleanUpTableList VALUES ('Database1','TableA')
INSERT INTO Database4..CleanUpTableList VALUES ('Database2','TableD')
INSERT INTO Database4..CleanUpTableList VALUES ('Database3', 'TableG')

清理脚本:

DECLARE @fetch_database_cursor INT
DECLARE @DatabaseName VARCHAR(50)

DECLARE DatabaseList CURSOR FOR 

    select name from sys.databases
    where
    name IN ('Database1','Database2', 'Database3'
            )   

OPEN DatabaseList
FETCH NEXT FROM DatabaseList INTO @DatabaseName

/* Keep track of the outer loop FETCH_STATUS in a local variable */

SET @fetch_database_cursor = @@FETCH_STATUS

/* Use outer loop FETCH_STATUS local variable as condition for outer WHILE  loop */

WHILE @fetch_database_cursor = 0
BEGIN

    DECLARE @ChangeDB VARCHAR(2500)
    DECLARE @TableName VARCHAR(50)
    DECLARE @ExecuteSQL VARCHAR(2500)
    DECLARE @fetch_table_cursor INT

    /* Change DB here */

    SET @ChangeDB = 'USE ' + @DatabaseName
    EXEC(@ChangeDB)

    /* Declare inner cursor */

    DECLARE TableList CURSOR FOR
        select table_name
        from information_schema.tables
        WHERE TABLE_TYPE = 'BASE TABLE'
        AND table_name NOT IN (
            SELECT TableName
            FROM Database4..CleanUpTableList
            WHERE DBName = @DatabaseName
            )
        ORDER BY table_name

    OPEN TableList
    FETCH NEXT FROM TableList INTO @TableName

    /* Store inner cursor fetch_status in local variable */

    SET @fetch_table_cursor = @@FETCH_STATUS

    /* Use inner cursor fetch_status local variable as condition for inner WHILE loop */

    WHILE @fetch_table_cursor = 0

        BEGIN
            SET @ExecuteSQL = 'DROP TABLE ' +@Tablename
            EXEC(@ExecuteSQL)
            SELECT @Tablename, 'Has Been Successfully Dropped'
            FETCH NEXT FROM TableList INTO @TableName
            SET @fetch_table_cursor=@@FETCH_STATUS

        END

    /* Close and deallocate inner cursor */

    CLOSE TableList
    DEALLOCATE TableList

    FETCH NEXT FROM DatabaseList INTO @DatabaseName
    SET @fetch_database_cursor = @@FETCH_STATUS
END

/* Close and deallocate outer cursor */

CLOSE DatabaseList
DEALLOCATE DatabaseList

如有任何建议,我们将不胜感激。

从您的代码中,我了解到您正在尝试在所有数据库中执行相同的操作,可以通过 sp_msforeachdb..

来实现

--所有数据库

 EXECUTE master.sys.sp_MSforeachdb 
 'USE [?];
 if db_id()<=4 return;
drop table dbo.sometable'

--运行只有几个数据库..

EXECUTE master.sys.sp_MSforeachdb 
'USE [?]; 
if db_name(db_id()) in (''master'',''tempdb'') --your dbnames
Begin 
select db_name() --your query
end'

您也可以使用 Aaron Bertrand's rewrite of Sp_msforeachDB which also can deal with some limitations of Sp_msforeachdb :Making a more reliable and flexible sp_MSforeachdb

与其尝试执行 Use database 语句,不如尝试在 drop 语句中完全限定 database.dbo.tablename。您拥有所有数据库和 table 名称。