要将NOT NULL列添加到包含许多记录的表中,需要应用DEFAULT约束。如果表非常大,则此约束会导致整个ALTER TABLE命令运行很长时间。这是因为:
假设:
可能的解决方案:
问题:
答案 0 :(得分:59)
我也为我的工作遇到了这个问题。我的解决方案是#2。
以下是我的步骤(我使用的是SQL Server 2005):
1)使用默认值将列添加到表中:
ALTER TABLE MyTable ADD MyColumn varchar(40) DEFAULT('')
2)使用NOT NULL
选项添加NOCHECK
约束。 NOCHECK
不对现有值强制执行:
ALTER TABLE MyTable WITH NOCHECK
ADD CONSTRAINT MyColumn_NOTNULL CHECK (MyColumn IS NOT NULL)
3)在表格中逐步更新值:
GO
UPDATE TOP(3000) MyTable SET MyColumn = '' WHERE MyColumn IS NULL
GO 1000
更新语句只会更新最多3000条记录。这允许当时保存一大块数据。我必须使用“MyColumn IS NULL”,因为我的表没有序列主键。
GO 1000
将执行前一个语句1000次。这将更新300万条记录,如果您需要更多,只需增加此数字。它将继续执行,直到SQL Server为UPDATE语句返回0条记录。
答案 1 :(得分:3)
以下是我要尝试的内容:
使用SIMPLE恢复模型不会停止记录,但会显着降低其影响。这是因为服务器在每次提交后都会丢弃恢复信息。
答案 2 :(得分:2)
你可以:
答案 3 :(得分:2)
只是用最新信息更新它。
在SQL Server 2012中,现在可以在以下情况下将其作为在线操作执行
对于第二个要求,示例可能是文字常量或诸如GETDATE()
之类的函数,它会为所有行计算相同的值。默认值为NEWID()
不符合条件,并且最终仍会更新所有行。
对于限定SQL Server的默认值对它们进行评估并将结果存储为列元数据中的默认值,因此这与创建的默认约束无关(如果不再需要,甚至可以删除它)。这可以在sys.system_internals_partition_columns
中查看。在下次碰巧更新之前,该值不会被写入行。
有关此内容的详细信息:online non-null with values column add in sql server 2012
答案 4 :(得分:0)
我认为这取决于您正在使用的SQL风格,但是如果您使用选项2,但是在最后的alter table表中使用默认值不为null会怎么样?
是否会很快,因为它看到所有值都不为空?
答案 5 :(得分:0)
如果您希望列在同一个表中,您只需要这样做。现在,选项3可能是最好的,因为在此操作进行时您仍然可以使数据库“活动”。如果你使用选项1,那么在操作发生时表被锁定,然后你真的被卡住了。
如果你真的不在乎列是否在表中,那么我认为分段方法是次佳的。虽然,我真的试图避免这种情况(我不这样做),因为就像Charles Bretana所说的那样,你必须确保并找到所有更新/插入该表并修改它们的地方。啊!
答案 6 :(得分:0)
我遇到了类似的问题,并选择了#2。 这种方式需要20分钟,而另一方面需要32小时!巨大的差异,谢谢你的提示。 我写了一篇关于它的完整博客文章,但这里是重要的sql:
Alter table MyTable
Add MyNewColumn char(10) null default '?';
go
update MyTable set MyNewColumn='?' where MyPrimaryKey between 0 and 1000000
go
update MyTable set MyNewColumn='?' where MyPrimaryKey between 1000000 and 2000000
go
update MyTable set MyNewColumn='?' where MyPrimaryKey between 2000000 and 3000000
go
..etc..
Alter table MyTable
Alter column MyNewColumn char(10) not null;
如果您有兴趣,请参阅博客文章: http://splinter.com.au/adding-a-column-to-a-massive-sql-server-table
答案 7 :(得分:0)
我有类似的问题,我采用了改进的#3方法。在我的情况下,数据库处于SIMPLE恢复模式,并且应该添加列的表未被任何FK约束引用。
我使用 SELECT ... INTO 语法,而不是使用相同的架构创建新表并复制原始表的内容。
根据微软(http://technet.microsoft.com/en-us/library/ms188029(v=sql.105).aspx)
SELECT ... INTO的日志记录量取决于恢复模型 对数据库有效。在简单的恢复模式或 批量记录恢复模型,批量操作记录最少。同 最小的日志记录,使用SELECT ... INTO语句可以更多 比创建一个表然后用一个表填充表有效 INSERT语句。有关更多信息,请参阅可以执行的操作 记录最少。
步骤顺序:
1.使用默认
添加新列时,将旧表中的数据移动到新表 SELECT table.*, cast (‘default’ as nvarchar(256)) new_column
INTO table_copy
FROM table
2.Drop旧表
DROP TABLE table
3.重新命名新创建的表
EXEC sp_rename 'table_copy', ‘table’
4.在新表上创建必要的约束和索引
在我的情况下,该表有超过1亿行,这种方法比方法#2完成得更快,而且日志空间增长很少。
答案 8 :(得分:0)
承认这是一个老问题。我的同事最近告诉我,他能够在一个包含13.6M行的表上的单个alter table语句中执行此操作。它在SQL Server 2012中完成了一秒钟。我能够在具有8M行的表上确认相同的内容。在SQL Server的更高版本中有什么变化?
dd
答案 9 :(得分:0)
1)使用默认值将列添加到表中:
<!DOCTYPE html>
<html>
<head>
<style>
div {
width: 100px;
height: 100px;
background-color: red;
-webkit-animation-name: example; /* Safari 4.0 - 8.0 */
-webkit-animation-duration: 4s; /* Safari 4.0 - 8.0 */
animation-name: example;
animation-duration: 4s;
}
/* Safari 4.0 - 8.0 */
@-webkit-keyframes example {
from {background-color: red;}
to {background-color: yellow;}
}
/* Standard syntax */
@keyframes example {
from {background-color: red;}
to {background-color: yellow;}
}
</style>
</head>
<body>
<p><b>Note:</b> This example does not work in Internet Explorer 9 and earlier versions.</p>
<div></div>
<p><b>Note:</b> When an animation is finished, it changes back to its original style.</p>
</body>
</html>
2)在表格中逐步更新值(与接受的答案效果相同)。调整要更新到您的环境的记录数,以避免阻止其他用户/进程。
ALTER TABLE MyTable ADD MyColumn int default 0
3)将列定义更改为require not null。在表未使用时(或安排几分钟的停机时间)运行以下命令。我已经成功地将它用于包含数百万条记录的表格。
declare @rowcount int = 1
while (@rowcount > 0)
begin
UPDATE TOP(10000) MyTable SET MyColumn = 0 WHERE MyColumn IS NULL
set @rowcount = @@ROWCOUNT
end
答案 10 :(得分:-1)
我会使用CURSOR而不是UPDATE。光标将批量更新所有匹配的记录,按记录记录 - 需要时间但不是锁表。
如果你想避免锁定,请使用WAIT。
我也不确定,DEFAULT约束会更改现有行。 可能NOT NULL约束与DEFAULT一起使用导致作者描述的情况。
如果更改,请将其添加到最后 所以伪代码看起来像:
-- without NOT NULL constrain -- we will add it in the end
ALTER TABLE table ADD new_column INT DEFAULT 0
DECLARE fillNullColumn CURSOR LOCAL FAST_FORWARD
SELECT
key
FROM
table WITH (NOLOCK)
WHERE
new_column IS NULL
OPEN fillNullColumn
DECLARE
@key INT
FETCH NEXT FROM fillNullColumn INTO @key
WHILE @@FETCH_STATUS = 0 BEGIN
UPDATE
table WITH (ROWLOCK)
SET
new_column = 0 -- default value
WHERE
key = @key
WAIT 00:00:05 --wait 5 seconds, keep in mind it causes updating only 12 rows per minute
FETCH NEXT FROM fillNullColumn INTO @key
END
CLOSE fillNullColumn
DEALLOCATE fillNullColumn
ALTER TABLE table ALTER COLUMN new_column ADD CONSTRAIN xxx
我确信存在一些语法错误,但我希望如此 帮助解决您的问题。
祝你好运!答案 11 :(得分:-3)
垂直分割表格。这意味着你将有两个表,具有相同的主键和完全相同的记录数...一个将是你已经拥有的,另一个将只有密钥,以及新的非空列(带有默认值) 。 修改所有插入,更新和删除代码,以便它们保持两个表同步...如果需要,您可以创建一个视图,将两个表“连接”在一起,以创建两个表的单个逻辑组合,看起来像一个客户选择表语句...