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

时间:2016-09-20 16:03:53

标签: sql-server cursor sql-server-2014 nested-loops

我正在编写一个脚本,如果在名为CleanUpTableList的表中不存在DB Name + Table Name组合,则执行该脚本时将删除给定DB列表中的任何表。所有DB都驻留在同一台服务器上。我正在使用SQL Server 2014。

我试图通过创建一个外部游标循环来执行此操作,该循环遍历数据库名称列表和一个内部游标循环,该循环拉入给定数据库中的表名列表,这些表名称在CleanUpTableList中找不到并删除这些表。但是,外部循环似乎无法更改数据库。该脚本仅访问起始数据库的相关表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

......这不是我想要的,所以我假设外循环中有些不对劲。我知道通常的DB更改命令是

USE Database1;
GO

但是我无法通过EXEC()弄清楚如何做到这一点。它一直告诉我GO附近有一个语法错误,我认为因为GO不能和“使用数据库1”在同一行;'我不知道使用EXEC()时的新行。我尝试使用

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

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

但这些也返回了语法错误。

以下是相关代码:

数据库/表创建脚本:

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

任何建议都表示赞赏。

2 个答案:

答案 0 :(得分:2)

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

- 所有数据库

gradlew assembleRelease -P production=flavor1

- 只运行少数数据库..

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

您还可以使用Aaron Bertrand重写Sp_msforeachDB,它也可以处理Sp_msforeachdb的一些限制:Making a more reliable and flexible sp_MSforeachdb

答案 1 :(得分:0)

尝试完全限定drop语句中的database.dbo.tablename,而不是尝试执行Use database语句。您拥有所有数据库和表名。