如何在SQL Server中的大表中添加NOT NULL列?

MrB*_*MrB 50 sql-server

要将NOT NULL列添加到具有许多记录的表中,需要应用DEFAULT约束.如果表非常大,则此约束会导致整个ALTER TABLE命令运行很长时间.这是因为:

假设:

  1. DEFAULT约束修改现有记录.这意味着db需要增加每条记录的大小,这会导致它将完整数据页上的记录转移到其他数据页,这需要时间.
  2. DEFAULT更新作为原子事务执行.这意味着需要增加事务日志,以便在必要时可以执行回滚.
  3. 事务日志跟踪整个记录.因此,即使只修改了一个字段,日志所需的空间也将基于整个记录的大小乘以现有记录的数量.这意味着向具有小记录的表添加列将比向具有大记录的表添加列更快,即使两个表的总记录数相同也是如此.

可能的解决方案:

  1. 把它吸干然后等待这个过程完成.只需确保将超时时间设置得很长.这样做的问题是,根据记录数量,可能需要数小时或数天才能完成.
  2. 添加列但允许NULL.然后,运行UPDATE查询以设置现有行的DEFAULT值.不要做UPDATE*.一次更新批量记录,否则您将遇到与解决方案#1相同的问题.这种方法的问题在于,当您知道这是一个不必要的选项时,最终会得到一个允许NULL的列.我相信有一些最佳实践文档说明你不应该有允许NULL的列,除非有必要.
  3. 使用相同的架构创建新表.将列添加到该架构.从原始表传输数据.删除原始表并重命名新表.我不确定这比#1更好.

问题:

  1. 我的假设是否正确?
  2. 这是我唯一的解决方案吗?如果是这样,哪一个是最好的?我不,我还能做什么?

DHo*_*out 59

我也为我的工作遇到了这个问题.我的解决方案是#2.

这是我的步骤(我正在使用SQL Server 2005):

1)使用默认值将列添加到表中:

ALTER TABLE MyTable ADD MyColumn varchar(40) DEFAULT('')
Run Code Online (Sandbox Code Playgroud)

2)NOT NULL使用NOCHECK选项添加约束.该NOCHECK不会对现有值执行:

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

3)在表格中逐步更新值:

GO
UPDATE TOP(3000) MyTable SET MyColumn = '' WHERE MyColumn IS NULL
GO 1000
Run Code Online (Sandbox Code Playgroud)
  • update语句仅更新最多3000条记录.这允许当时保存一大块数据.我必须使用"MyColumn IS NULL",因为我的表没有序列主键.

  • GO 1000将执行前一个语句1000次.这将更新300万条记录,如果您需要更多,只需增加此数字.它将继续执行,直到SQL Server返回UPDATE语句的0条记录.

  • 我不知道你可以在GO之后输入一个数字.你改变了我的世界. (55认同)
  • 值得一提的是,MSDN强烈反对使用`WITH NOCHECK`:"如果您不想验证针对现有数据的新CHECK或FOREIGN KEY约束,请使用WITH NOCHECK.除极少数情况外,我们不建议这样做.新约束将在所有后续数据更新中进行评估.添加约束时由WITH NOCHECK抑制的任何约束违规都可能导致将来更新失败,如果他们使用不符合约束的数据更新行. (3认同)
  • 可能值得将以下内容添加到此答案的底部,以将约束标记为已检查:ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyColumn_NOTNULL - http://sqlblog.com/blogs/hugo_kornelis/archive/2007/03/29/can-你,信任你,constraints.aspx (3认同)
  • 此外:"查询优化器不考虑使用NOCHECK定义的约束.在使用ALTER TABLE <table> WITH CHECK CHECK CONSTRAINT ALL重新启用它们之前,将忽略这些约束." (2认同)
  • [马丁史密斯显示为什么NOCHECK在这里是一个问题](http://dba.stackexchange.com/a/48936/1186). (2认同)