2016-09-20 33 views
1

我正在編寫一個腳本,該腳本在執行時將在給定DB列表中刪除任何表,如果DB名稱+表名稱組合不存在一個名爲CleanUpTableList的表。所有數據庫駐留在同一臺服務器上。我正在使用SQL Server 2014.嘗試使用遊標循環更改數據庫,但數據庫不會更改

我想通過創建一個外部遊標循環來循環訪問數據庫名稱列表和一個內部遊標循環,該遊標循環在給定數據庫中引入表名稱列表沒有在CleanUpTableList中找到並刪除這些表。但是,似乎外層循環無法更改數據庫。該腳本只能訪問X次啓動數據庫的相關表,其中X是外部遊標中的許多數據庫名稱條目。因此,舉例來說,如果我開始在數據庫1,我在我的外光標有三個數據庫名稱條目,而不是得到:

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 /表創建腳本:

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 ' [email protected] 
      EXEC(@ExecuteSQL) 
      SELECT @Tablename, 'Has Been Successfully Dropped' 
      FETCH NEXT FROM TableList INTO @TableName 
      SET @[email protected]@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

從您的代碼,我明白你正在嘗試做的所有數據庫中相同的操作,可以通過sp_msforeachdb實現..

--all數據庫

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

--run只有少數數據庫..

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

您還可以使用Sp_msforeachDB的Aaron Bertrand的改寫也可以處理Sp_msforeachdb的一些限制:Making a more reliable and flexible sp_MSforeachdb

+0

我正在嘗試僅爲服務器上選定的幾個數據庫執行此操作。只涉及明確指定的數據庫是非常重要的,因爲服務器上的其他數據庫在CleanUpTableList中沒有任何條目,因此通過此腳本放置它們會導致意外的表刪除。 我假設我可以使用語句的db_id()部分顯式標識這些數據庫?像 'EXECUTE master.sys.sp_MSforeachdb'USE [?];如果db_id()IN(1,2,3)返回; drop table dbo.sometable' –

+0

針對您的特定場景查看已更新 – TheGameiswar

0

而不是嘗試執行使用數據庫語句,請嘗試在drop語句中完全限定database.dbo.tablename。你有所有的數據庫和表名。