快速将 NULL 列更改为 NOT NULL

Jos*_*gle 12 sql-server-2008 null alter-table

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

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;
Run Code Online (Sandbox Code Playgroud)

相对而言,查询需要永远。它实际上需要 10 到 20 分钟,是添加检查约束的两倍多。有没有办法立即更新该列的表元数据,尤其是因为我知道该列没有任何行具有 NULL 值?

Mar*_*ith 12

@ypercube 的答案确实部分地管理了这一点,因为元数据只发生了变化。

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

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

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

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

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)
Run Code Online (Sandbox Code Playgroud)

计划

将此与更简单的进行比较

ALTER TABLE T ALTER COLUMN X INT NOT NULL

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)
Run Code Online (Sandbox Code Playgroud)

计划

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

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


ype*_*eᵀᴹ 7

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

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;
Run Code Online (Sandbox Code Playgroud)

  • 我没有更好的解决方案。我赞成这一点,因为它提供了部分解决方案。如果所有 OP 想要做的是防止无效数据,它就会起作用(并且应该比“ALTER COLUMN”更快,因为一旦获得了“Sch-M”锁,就根本不需要扫描行)。只是指出它并不完全相同(例如,如果在“NOT IN”查询中使用,计划将更加复杂) (2认同)