1

每周我的客户都会向我发送一个数据库,其中包含一个非常大的表,没有主键,并且其中具有唯一 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 秒后失败,并显示错误消息:

无法对表“订单”中的可为空列定义 PRIMARY KEY 约束。[SQLSTATE 42000](错误 8111)无法创建约束或索引。请参阅以前的错误。[SQLSTATE 42000](错误 1750)。步骤失败。

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

是什么赋予了?我写了几十个存储过程,但没有一个是那样的?

我能做些什么来强迫它等待?我以为这就是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

哪个没有错误:)

4

1 回答 1

1

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

两种选择:

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

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

于 2020-02-23T08:15:49.323 回答