2009-08-27 38 views
1

對於我們的SQL Server數據庫,我們使用版本控制方案來跟蹤模式更新。這個想法是,你應該能夠運行這個腳本,將模式從任何以前的版本升級到當前版本。再次運行主腳本應該只執行最新的模式更新。T-SQL「可重新運行」數據庫更新腳本 - 刪除列

劇本的結構是這樣的:

SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=0 
IF (@Installed IS NULL) 
BEGIN 
    ... 
    INSERT INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 0, GetDate()) 
END 
ELSE PRINT 'Version 1.0.0 was already installed on ' + Convert(varchar(10), @Installed) 

SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=1 
IF (@Installed IS NULL) 
BEGIN 
    ... 
    INSERT INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 1, GetDate()) 
END 
ELSE PRINT 'Version 1.0.1 was already installed on ' + Convert(varchar(10), @Installed) 

這通常工作得很好。但是,當模式更新DROP包含在前一個INSERT中的列時,我們遇到了一個問題;也就是說,我們有這樣的東西:

SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=0 
IF (@Installed IS NULL) 
BEGIN 
    INSERT [foo] ([a], [b], [OrganizationId]) VALUES (N'a', N'b', N'1'); 
    INSERT INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 0, GetDate()); 
END 
ELSE PRINT 'Version 1.0.0 was already installed on ' + Convert(varchar(10), @Installed) 

SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=1 
IF (@Installed IS NULL) 
BEGIN 
    ALTER TABLE [foo] DROP COLUMN [OrganizationId]; 
    INSERT INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 1, GetDate()); 
END 
ELSE PRINT 'Version 1.0.1 was already installed on ' + Convert(varchar(10), @Installed) 

這工作正常,它第一次執行;版本1.0.1被執行,並且該列被刪除。然而,運行腳本第二次產量:

 Msg 207, Level 16, State 1, Line 7118 
    Invalid column name 'OrganizationId'. 

也就是說,即使沒有被執行版本1.0.0塊內的INSERT,它仍然被解析併產生了無效的列錯誤。

有關如何解決此問題的任何建議?理想情況下,我希望使用條件保護INSERT,以便它甚至不被解析,但似乎沒有發生。我可以在sp_ExecuteSql()調用內動態執行INSERT,但我不想(需要大量的改進)。

謝謝 -

--Andy

回答

1

好吧,我最初錯誤地閱讀了這個問題。 :-)

如果更改插入線:

INSERT [foo] ([a], [b], [OrganizationId]) VALUES (N'a', N'b', N'1'); 

到:

exec('INSERT [foo] ([a], [b], [OrganizationId]) VALUES (''a'', ''b'', ''1'')'); 

你不應該有問題,因爲SQL 「文本」 內部EXEC贏得」 t被解析,直到 exec()被實際調用。

+0

謝謝,羅恩。我希望能有一種替代方案來編輯​​一堆現有的生成腳本,但至少有一條路向前進。 – 2009-08-27 17:05:41

1

不幸的是,這是類似於你當(一個存儲過程中)的問題,您刪除臨時表,然後重新創建它。解析器會抱怨它已經存在,而不會意識到臨時表剛剛被刪除。

如果您將它與GO語句分開,那麼您應該發現系統會重新評估每個部分。

Rob

0

我們使用幾乎相同的設置來處理版本化模式。

一般而言,您的方法完全合理。我們幾年來一直在運行這個通用設置。基本上,爲了處理任何破壞性或不兼容的模式更改,我們將這些補丁作爲自動CruiseControl.NET版本的一部分運行。

所以我們的數據庫建立看起來像這樣...

  • 從目前的生產版本的備份還原。
  • 檢查恢復的數據庫版本
  • 運行所有的修補程序(這些都是按照約定使用major.minor.sql命名的),這些修補程序晚於[版本]表中指示的版本。

這樣,我們無論修補程序如何,都可以整天重建,而不會出現任何問題。這也確保了當我們部署到PRODUCTION時,沒有問題,因爲我們已經在開發過程中部署了PRODUCTION db 1000x。

0

您是否試過動態Sql? 不幸的是,解析器在運行之前會檢查整個腳本,因此任何無效的列都會停止執行。