存储过程中的 ALTER COLUMN NOT NULL 总是非顺序执行?

ALTER COLUMN NOT NULL in a stored procedure always executes non-sequentially?

我的客户每周都会给我发送一个数据库,其中有一个非常大的 table,没有主键,并且其中具有唯一 ID 的列可以为空。

所以我运行ALTER TABLE将我想作为主键的列设置为NOT NULL:

ALTER TABLE Live1.dbo.Orders 
    ALTER COLUMN OrderID varchar(10) NOT NULL;

然后在大约 45 分钟后完成并添加我的主键:

ALTER TABLE Live1.dbo.Orders 
    ADD CONSTRAINT PK_OrdersOrderID PRIMARY KEY CLUSTERED (OrderID)

到目前为止一切都很好。

在那之前,我尝试将这个冗长乏味的操作自动化。

我创建了一个非常简单的存储过程,运行我的客户的数据库完成恢复后,作为代理作业的一部分:

CREATE PROCEDURE dbo.AddPKey
AS
BEGIN
    SET NOCOUNT ON;

    ALTER TABLE Live1.dbo.Orders ALTER COLUMN OrderID varchar(10) NOT NULL;
    ALTER TABLE Live1.dbo.Orders ADD CONSTRAINT PK_OrdersOrderID PRIMARY KEY CLUSTERED (OrderID)
END
GO

1 秒后失败,错误消息:

Cannot define PRIMARY KEY constraint on nullable column in table 'Orders'. [SQLSTATE 42000] (Error 8111) Could not create constraint or index. See previous errors. [SQLSTATE 42000] (Error 1750). The step failed.

所以它试图在前面的 ALTER COLUMN 步骤完成之前设置主键约束。它应该至少在 45 分钟后才尝试添加主键约束,但它会尝试立即执行。如果我手动 运行 ALTER COLUMN,它工作得很好。

什么给了?我写了几十个存储过程,none 其中的行为是这样的?

我该怎么做才能让它等待?我认为那是 ALTER COLUMN 行末尾的分号的作用?

更新:

非常感谢 π 对这里实际发生的事情的深刻回答和澄清。我的最终存储过程如下所示:

DECLARE @DynamicSQL nvarchar(4000)
 ALTER TABLE Live1.dbo.Orders ALTER COLUMN OrderID varchar(10) NOT NULL;
SET @DynamicSQL = 'ALTER TABLE Live1.dbo.Orders
                     ADD CONSTRAINT PK_OrdersOrderID PRIMARY KEY CLUSTERED (OrderID);'
EXEC Live1.sys.sp_executesql @DynamicSQL

没有错误:)

这是因为在解析语句时,SQL 服务器解析第二条语句时该列仍然可以为空。

两个选项:

  • 运行 第二个语句作为动态 SQL 查询使用 sp_executesql。这将创建一个单独的上下文,当该查询被解析时,第一个已经有 运行.
  • 创建一个仅用于添加主键的存储过程。执行那个代替直接 alter/add 约束语句。

PS:分号用于在语言级别分隔(或终止)语句。它没有 运行 时间功能。