Sql server 如何使用单个ALTERTABLE语句将带有check约束的新列添加到表中?

Sql server 如何使用单个ALTERTABLE语句将带有check约束的新列添加到表中?,sql-server,Sql Server,我正在编写一个小工具,为我生成一些SQL语句。声明如下所示: SET IMPLICIT_TRANSACTIONS ON; BEGIN TRANSACTION x; ALTER TABLE schema.MyTable ADD MyNewColumn INT DEFAULT(-7777); --ALTER TABLE schema.MyTable ADD CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5)

我正在编写一个小工具,为我生成一些SQL语句。声明如下所示:

SET IMPLICIT_TRANSACTIONS ON;
BEGIN TRANSACTION x;

ALTER TABLE schema.MyTable ADD MyNewColumn INT DEFAULT(-7777);

--ALTER TABLE schema.MyTable ADD CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5));

EXEC sp_addextendedproperty @name = N'MS_Description', @value = 'texttexttext',  
@level0type = N'Schema',   @level0name = 'schema',
@level1type = N'Table',    @level1name = 'MyTable',
@level2type = N'Column',   @level2name = 'MyNewColumn';

INSERT INTO schema.SomeOtherTable VALUES(1, 2, 3);
-- Other statements....

--ROLLBACK TRANSACTION x;
--COMMIT TRANSACTION x;
表名和类似的东西来自工具界面。带有
ADD约束的行因
无效列'MyNewColumn'
而失败。。。我想那是因为未经签署的交易。。。但我需要的是,如果其他一些语句失败,那么应该删除列、约束和其他数据


所以。。。是否有一种方法可以
向现有表中添加一个新列,也可以
在一条语句中将一个命名的
检查约束添加到同一个表中?或者您有其他解决方法吗?

问题不是由于未提交的事务,而是因为在执行之前必须编译整个批,并且DDL中引用的列还不存在

为了避免错误,可以在单个语句中添加列和约束:

ALTER TABLE dbo.MyTable
     ADD MyNewColumn INT DEFAULT(-7777)
    ,CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5));
如果您的工具不支持此构造,则需要在单独的批中或在同一批中使用动态SQL执行语句,如:

ALTER TABLE dbo.MyTable
     ADD MyNewColumn INT DEFAULT(-7777);
EXECUTE(N'ALTER TABLE dbo.MyTable ADD CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5));');

此外,我建议您删除
SET IMPLICIT_TRANSACTIONS ON因为脚本中已经有显式事务。否则,在
COMMIT

之后仍然会有一个未提交的事务。问题不是由于未提交的事务,而是因为在执行之前必须编译整个批,并且DDL中引用的列还不存在

为了避免错误,可以在单个语句中添加列和约束:

ALTER TABLE dbo.MyTable
     ADD MyNewColumn INT DEFAULT(-7777)
    ,CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5));
如果您的工具不支持此构造,则需要在单独的批中或在同一批中使用动态SQL执行语句,如:

ALTER TABLE dbo.MyTable
     ADD MyNewColumn INT DEFAULT(-7777);
EXECUTE(N'ALTER TABLE dbo.MyTable ADD CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5));');

此外,我建议您删除
SET IMPLICIT_TRANSACTIONS ON因为脚本中已经有显式事务。否则,在
COMMIT

向我的注释添加更多上下文之后,您仍然会有一个未提交的事务:“要影响列(即
在其中插入
,添加扩展属性等),它需要在不同的批处理中。即使提交事务也不会改变这一点。在该批处理完成之前,您将无法对该列执行“操作”

我的说法实际上是错误的。出现错误的原因是,添加列并尝试插入(或添加
约束
)的批实际上失败。编译器分析SQL,并查看您是否试图将
插入到当前不存在的列中(向该列添加
约束),因此失败

举个例子:

CREATE TABLE dbo.MyTable (ID int);
GO

ALTER TABLE dbo.MyTable ADD MyColumn int DEFAULT (100);

INSERT INTO dbo.MyTable
VALUES(1,100);
此操作失败,错误如下:

提供的值的列名或数目与表定义不匹配

但是,如果命名列,则会出现不同的错误:

ALTER TABLE dbo.MyTable ADD MyColumn int DEFAULT (100);

INSERT INTO dbo.MyTable  (ID, MyColumn)
VALUES(1,100)
无效的列名“MyColumn”

但是,对于SP
SP_addextendedproperty
,在执行SP之前,会延迟检查列的存在性;此时该列将存在。如果尝试以下操作,则不会出现错误:

CREATE TABLE dbo.MyTable (ID int);
GO

ALTER TABLE dbo.MyTable ADD MyColumn int DEFAULT (100);

EXEC sp_addextendedproperty @name = N'MS_Description', @value = 'texttexttext',  
                            @level0type = N'Schema',   @level0name = 'dbo',
                            @level1type = N'Table',    @level1name = 'MyTable',
                            @level2type = N'Column',   @level2name = 'MyColumn';

GO
INSERT INTO dbo.MyTable  (ID, MyColumn)
VALUES(1,100)

GO

DROP TABLE dbo.MyTable;
对于您的
约束
,这与
插入
的问题相同。它需要在一个单独的批次

一种方法是使用
sp\u executesql
,推迟语句的编译,同时添加
约束:

ALTER TABLE schema.MyTable ADD MyNewColumn INT DEFAULT(-7777);

EXEC sp_executesql N'ALTER TABLE schema.MyTable ADD CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5));';

在我的评论中添加更多的上下文:“要影响某个列(即,
INSERT
、添加扩展属性等),它需要位于不同的批处理中。即使提交事务也不会改变这一点。在该批处理完成之前,您将无法对该列执行“操作”

我的说法实际上是错误的。出现错误的原因是,添加列并尝试插入(或添加
约束
)的批实际上失败。编译器分析SQL,并查看您是否试图将
插入到当前不存在的列中(向该列添加
约束),因此失败

举个例子:

CREATE TABLE dbo.MyTable (ID int);
GO

ALTER TABLE dbo.MyTable ADD MyColumn int DEFAULT (100);

INSERT INTO dbo.MyTable
VALUES(1,100);
此操作失败,错误如下:

提供的值的列名或数目与表定义不匹配

但是,如果命名列,则会出现不同的错误:

ALTER TABLE dbo.MyTable ADD MyColumn int DEFAULT (100);

INSERT INTO dbo.MyTable  (ID, MyColumn)
VALUES(1,100)
无效的列名“MyColumn”

但是,对于SP
SP_addextendedproperty
,在执行SP之前,会延迟检查列的存在性;此时该列将存在。如果尝试以下操作,则不会出现错误:

CREATE TABLE dbo.MyTable (ID int);
GO

ALTER TABLE dbo.MyTable ADD MyColumn int DEFAULT (100);

EXEC sp_addextendedproperty @name = N'MS_Description', @value = 'texttexttext',  
                            @level0type = N'Schema',   @level0name = 'dbo',
                            @level1type = N'Table',    @level1name = 'MyTable',
                            @level2type = N'Column',   @level2name = 'MyColumn';

GO
INSERT INTO dbo.MyTable  (ID, MyColumn)
VALUES(1,100)

GO

DROP TABLE dbo.MyTable;
对于您的
约束
,这与
插入
的问题相同。它需要在一个单独的批次

一种方法是使用
sp\u executesql
,推迟语句的编译,同时添加
约束:

ALTER TABLE schema.MyTable ADD MyNewColumn INT DEFAULT(-7777);

EXEC sp_executesql N'ALTER TABLE schema.MyTable ADD CONSTRAINT CHK_MyTable_MyNewColumn CHECK(MyNewColumn IN(1, 2, 3, 4, 5));';

请尝试查看嵌套事务。您可以打开一个大型事务,然后在其中打开一个事务,在其中提交架构更改、提交或回滚,然后打开第二个事务以添加数据、提交或回滚。BEGIN TRAN Top_TRAN BEGIN TRAN Schema_Chg COMMIT TRAN Schema_Chg BEGIN TRAN Data_Ins COMMIT TRAN Data_Ins。事务在这里没有帮助,@RaduGheorghiu,这是一个编译错误;事务将永远不会启动。请尝试查看嵌套事务。您可以打开一个大型事务,然后在其中打开一个事务,在其中提交架构更改、提交或回滚,然后打开第二个事务以添加数据、提交或回滚。BEGIN TRAN Top_TRAN BEGIN TRAN Schema_Chg COMMIT TRAN Schema_Chg BEGIN TRAN Data_Ins COMMIT TRAN Data_Ins。事务在这里没有帮助,@RaduGheorghiu,这是一个编译错误;这个