2015-11-05 25 views
2

裏面我有設計成是「安全」在不同的情況下運行,或者在同一實例多次,而不會造成任何不良數據或錯誤「無效列名」拋出可達腳本塊

大型SQL腳本

編寫這樣的腳本時,我總是依賴於這樣的語法:

if not exists (select 1 from SYS.FOREIGN_KEYS where NAME = 'FK_BAR') 
begin 
    alter table [MyTable] 
    add constraint FK_BAR foreign key (some_id) references Other_Table(some_id) 
end 
go 

這通常工作得很好。但是,最近我遇到了一個場景,我無法防止在執行過程中拋出錯誤。在下面的代碼中,列「deprecated_column」已經從表foo下降:

if  exists (select 1 from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'Foo' and COLUMN_NAME = 'deprecated_column') 
    and exists (select 1 from [Foo] where new_column is null) 
begin 
    declare @updated int set @updated = 1; 

    while @updated > 0 
    begin 
     update top (500000) 
       Foo 
      set new_column = deprecated_column 
     where new_column is null 
     ; 
     set @updated = @@rowcount; 
    end 
end 
go 

如果我運行if獨立於本計劃內兩個exist的,他們返回‘無結果’的預期,這意味着內部代碼永遠不會被執行。但是,當我運行該腳本時,服務器將引發錯誤:Invalid column name 'deprecated_column'.,並且該腳本被標記爲完成並顯示錯誤,這會導致在我們的系統上引發警報(即,DBA被通知並且必須檢查),其中正在導致一些不必要的開銷,應該是一個簡單的自動化任務。

是否有一些我忽略了的語法會讓這段代碼在所有情況下都不會出錯?

+0

@SeanLange這是不正確你剛纔說的話,放在IF塊內的任何有效的SQL,如果控制從來沒有進入塊也不會有例外。 –

+0

@ M.Ali你是對的......刪除我的評論。 :( –

+1

這裏肯定有一個挑戰,如果被引用的表不存在,代碼將會正確編譯,但如果表存在,那麼它也會檢查被引用的列是否存在。延遲名稱解析 –

回答

2

正如我在評論中所解釋的,T-SQL是一種編譯語言,「Invalid Column」是一個編譯器錯誤,而不是執行錯誤。但是,由於大多數T-SQL只是在執行之前編譯,所以通常可以看到它。

由於T-SQL試圖編譯所有的代碼而不管IF分支,所以解決這個問題的唯一直接方法就是使用動態SQL。像這樣:

if  exists (select 1 from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'Foo' and COLUMN_NAME = 'deprecated_column') 
    and exists (select 1 from [Foo] where new_column is null) 
begin 
    EXEC(' 
     declare @updated int set @updated = 1; 

     while @updated > 0 
     begin 
      update top (500000) 
        Foo 
       set new_column = deprecated_column 
      where new_column is null 
      ; 
      set @updated = @@rowcount; 
     end 
    '); 
end 
go