快速将NULL列更改为NOT NULL


11

我有一个包含数百万行的表和一个允许NULL值的列。但是,当前没有任何行具有该列的NULL值(我可以通过查询很快地验证这一点)。但是当我执行命令时

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;

相对而言,查询将永远花费。实际上,它需要10到20分钟,是添加检查约束的两倍多。有没有一种方法可以立即更新该列的表元数据,特别是因为我知道该列没有行具有NULL值?


2
否(或至少不使用文档/受支持的方法)。请参阅为什么将ALTER COLUMN设置为NOT NULL会导致大量日志文件增长?
马丁·史密斯

2
Sch-M当它花费“永远”时,它也可能正在等待锁。您是否看过它是在等待还是忙?
马丁·史密斯

@MartinSmith我永远澄清了我的意思。我正在开发环境中对此进行测试,没有其他会话影响数据库。最终确实完成了。但是您链接的答案说明了为什么要花这么长时间。如果您可以将您的评论改写为答案,那么我会接受的。
约瑟夫·戴格尔,2013年

Answers:


12

@ypercube的答案确实部分解决了此问题,因为仅元数据发生了变化。

添加带有的约束NOCHECK意味着不需要读取任何行来验证它,并且如果您从该列不包含NULL值的位置开始(并且如果您知道在检查和添加约束之间将不会添加任何内容),因为约束阻止NULL了将来INSERTUPDATE操作中创建值,所以这将起作用。

但是,添加约束仍然可能对并发事务产生影响。在ALTER TABLE将需要获得一个Sch-M第一锁。在等待时,所有其他表访问将如此处所述被阻止。

一旦Sch-M获得了锁,操作应该很快。

这样做的一个问题是,即使您知道该列实际上没有NULL,查询优化器也不信任该约束,这意味着计划可能不是最优的。

CREATE TABLE T (X INT NULL)

INSERT INTO T 
SELECT ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values

ALTER TABLE T WITH NOCHECK
  ADD  CONSTRAINT X_NOT_NULL 
    CHECK (X IS NOT NULL) ; 

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

计划

比较简单一点

ALTER TABLE T ALTER COLUMN X INT NOT NULL

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

计划

用这种方式更改列定义时可能遇到的一个问题是,它不仅需要读取所有行以验证它们是否满足条件,而且最终可能会对行进行实际记录的更新

一个可能的中途选择可能是添加检查约束WITH CHECK。这将比WITH NOCHECK需要读取所有行的速度慢,但是它确实允许查询优化器在上面的查询中给出更简单的计划,并且应避免可能的记录更新问题。


7

您可以CHECK使用以下NOCHECK选项添加表约束,而不用更改列:

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;

1
这将防止将来进行更新或插入而构成该列,NULL但将不能被查询优化器使用。
马丁·史密斯

@MartinSmith哦,是的,我只是阅读了类似问题的答案和评论:如何在SQL Server中的大型表中添加NOT NULL列?请添加问题的答案或更好的解决方案,我将删除我的问题。
ypercubeᵀᴹ

2
我没有更好的解决方案。我对此表示赞同,因为它提供了部分解决方案。如果OP想要做的只是防止无效数据,它就会起作用(并且比获取锁后要快得多ALTER COLUMNSch-M这根本不需要扫描行)。只是指出它并不完全相同(例如,如果在NOT IN查询中使用,该计划将更加复杂)
Martin Smith
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.