T-SQL“可重新运行”的数据库更新脚本 - 列删除

时间:2009-08-27 00:51:50

标签: tsql scripting

对于我们的SQL Server数据库,我们使用版本控制方案来跟踪架构更新。我们的想法是,您应该能够运行此脚本,以将架构从任何先前版本升级到当前版本。再次运行主脚本应该只执行最新的架构更新。

脚本的结构如下:

 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)

这通常效果很好。但是,当架构更新DROPs包含在先前的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,但我不愿意(需要进行大量的改造)。

谢谢 -

- 安迪

4 个答案:

答案 0 :(得分:1)

不幸的是,这与您在删除临时表然后重新创建它时(在存储过程中)时遇到的问题类似。解析器会抱怨它已经存在,似乎没有意识到临时表刚被删除。

如果您将其与GO语句分开,那么您应该会发现系统将重新评估每个部分。

罗布

答案 1 :(得分:1)

好的,我最初误读了这个问题。 : - )

如果您更改插入行:

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

为:

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

你不应该有这个问题,因为exec中的SQL“text”将不会被解析 exec()实际上是被调用的。

答案 2 :(得分:0)

我们使用几乎相同的设置来处理架构的版本控制。

一般来说,你的方法完全合理。几年来,我们一直在使用这种常规设置。基本上,为了处理任何破坏性或不兼容的模式更改,我们将补丁作为自动CruiseControl.NET构建的一部分运行。

所以我们的数据库构建看起来像这样......

  • 从当前的PRODUCTION版本备份恢复。
  • 检查已恢复数据库的版本
  • 运行所有补丁(这些补丁通过使用major.minor.sql的约定命名),这些补丁晚于[Versions]表中指示的版本。

这样,无论补丁做什么,我们都可以整天重建而不会出现任何问题。这也确保了当我们部署到PRODUCTION时没有问题,因为我们已经在开发期间部署了PRODUCTION db 1000x。

答案 3 :(得分:0)

你试过动态Sql吗? 遗憾的是,解析器将在运行之前检查整个脚本,因此任何无效列都将停止执行。