2013-04-23 105 views
8

我开发的SQL脚本,使用SSMS,这使得在数据库中的一些变化后:“无效列名称”错误调用时插入表中创建

USE MyDatabase; 

BEGIN TRANSACTION; 

-- some statements 

PRINT(N'#1'); 

IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = N'dbo' AND TABLE_NAME = N'Table1' AND COLUMN_NAME = 'Table2_Id')) 
BEGIN 
    ALTER TABLE [dbo].[Table1] DROP CONSTRAINT [FK_Table1_Table2_Table2_Id]; 
    ALTER TABLE [dbo].[Table1] DROP COLUMN [Table2_Id]; 
    DROP TABLE [dbo].[Table2]; 

    PRINT(N'Table2 was dropped.'); 
END 

PRINT(N'#2'); 

IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = N'dbo' AND TABLE_NAME = N'Table2')) 
BEGIN 
    CREATE TABLE [dbo].[Table2] 
    (
     [Id] INT NOT NULL PRIMARY KEY IDENTITY, 
     [Number] INT NOT NULL UNIQUE, 
     [Name] NVARCHAR(200) NOT NULL, 
     [RowVersion] TIMESTAMP NOT NULL 
    ); 
PRINT(N'Table2 was re-created.'); 
    INSERT INTO [dbo].[Table2]([Number], [Name]) VALUES(-1, N'Default value'); 
PRINT(N'Default value was inserted in Table2.'); 
END 

-- some statements 

COMMIT TRANSACTION; 

如果Table1有一个专栏,叫Table2_Id,那么数据库有两个表(Table1Table2)以及它们之间的外键关系。在这种情况下,我需要:

  • 放弃外键关系FK_Table1_Table2_Table2_Id;
  • 删除外键列Table1.Table2_Id;
  • drop Table2;
  • 重新创建Table2,使用新的表模式;
  • Table2中插入一些默认值。

当我试图执行这个脚本,我得到这些错误:

消息207,级别16,状态1,行262无效列名 '数字'。
消息207,级别16,状态1,行262无效的列名称'名称'。

看起来像SQL Server使用旧架构为Table2(这的确还没有这些列),但是这怎么可能,如果表刚刚用新模式创建?

我在做什么错?

服务器版本是SQL Server 2012(SP1) - 11.0.3128.0(X64)。

UPDATE

我已添加PRINT调用(请参阅上面的脚本)。消息窗口中没有任何内容,除了错误消息。所以,脚本没有被执行......发生了什么事?

回答

11

SQL Server尝试编译整个批处理。如果该表已经存在,那么它将根据预先存在的定义进行编译。引用新列的语句不会编译,因此批处理从不执行。

您需要将使用新定义的语句分组到新批次中。如果您在SSMS运行此只需插入GO

USE MyDatabase; 

BEGIN TRANSACTION; 

-- some statements 

PRINT(N'#1'); 

IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = N'dbo' AND TABLE_NAME = N'Table1' AND COLUMN_NAME = 'Table2_Id')) 
BEGIN 
    ALTER TABLE [dbo].[Table1] DROP CONSTRAINT [FK_Table1_Table2_Table2_Id]; 
    ALTER TABLE [dbo].[Table1] DROP COLUMN [Table2_Id]; 
    DROP TABLE [dbo].[Table2]; 

    PRINT(N'Table2 was dropped.'); 
END 

GO 

PRINT(N'#2'); 

IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = N'dbo' AND TABLE_NAME = N'Table2')) 
BEGIN 
    CREATE TABLE [dbo].[Table2] 
    (
     [Id] INT NOT NULL PRIMARY KEY IDENTITY, 
     [Number] INT NOT NULL UNIQUE, 
     [Name] NVARCHAR(200) NOT NULL, 
     [RowVersion] TIMESTAMP NOT NULL 
    ); 
PRINT(N'Table2 was re-created.'); 
    INSERT INTO [dbo].[Table2]([Number], [Name]) VALUES(-1, N'Default value'); 
PRINT(N'Default value was inserted in Table2.'); 
END 

COMMIT 

否则,你可以在孩子批次运行出错的行

EXEC(N'INSERT INTO [dbo].[Table2]([Number], [Name]) VALUES(-1, N''Default value'');') 
+0

感谢您的解释......这 - “如果该表已经存在那么它将按照先前存在的定义进行编译“ - 是我所能想象到的最愚蠢且不明显的策略...”GO“不是一个好选择,脚本比较大,并且有一些变量,在脚本的顶部声明,在整个脚本中广泛使用。无论如何,谢谢,我会考虑解决方法。 – Dennis 2013-04-23 10:53:56

+0

@ Dennis如果你不想失去你的变量,那么我建议将不同的批处理(删除/创建表)分成不同的存储过程,然后将它们包装在主存储过程中。 – 2013-08-08 01:55:10