存储过程中的 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:分号用于在语言级别分隔(或终止)语句。它没有 运行 时间功能。
我的客户每周都会给我发送一个数据库,其中有一个非常大的 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:分号用于在语言级别分隔(或终止)语句。它没有 运行 时间功能。