对于我们的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,但我不愿意(需要进行大量的改造)。
谢谢 -
- 安迪
答案 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时没有问题,因为我们已经在开发期间部署了PRODUCTION db 1000x。
答案 3 :(得分:0)
你试过动态Sql吗? 遗憾的是,解析器将在运行之前检查整个脚本,因此任何无效列都将停止执行。