我有一个64m行的表,在磁盘上占用4.3 GB的数据.
每行大约30个字节的整数列,以及用于文本的变量NVARCHAR(255)列.
我添加了一个数据类型为Datetimeoffset(0)的NULLABLE列.
然后,我为每一行更新了此列,并确保所有新插入都在此列中放置一个值.
一旦没有NULL条目,我就运行此命令使我的新字段成为必填项:
ALTER TABLE tblCheckResult ALTER COLUMN [dtoDateTime] [datetimeoffset](0) NOT NULL
结果是事务日志大小大幅增长 – 从6GB到超过36GB,直到空间不足!
解决方法
当您将列更改为NOT NULL时,即使没有NULL值,sql Server也必须触摸每个页面.根据您的填充因子,这实际上可能会导致大量页面拆分.当然,每个被触摸的页面都必须被记录,我怀疑由于分割,可能需要为许多页面记录两个更改.但是,由于这一切都是在一次通过中完成的,因此日志必须考虑所有更改,因此,如果您点击取消,它就会知道要撤消的内容.
一个例子.简单表:
DROP TABLE dbo.floob; GO CREATE TABLE dbo.floob ( id INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,bar INT NULL ); INSERT dbo.floob(bar) SELECT NULL UNION ALL SELECT 4 UNION ALL SELECT NULL; ALTER TABLE dbo.floob ADD CONSTRAINT df DEFAULT(0) FOR bar
现在,让我们看看页面详细信息.首先,我们需要找出我们正在处理的页面和DB_ID.在我的例子中,我创建了一个名为foo的数据库,DB_ID恰好是5.
DBCC TRACEON(3604,-1); DBCC IND('foo','dbo.floob',1); SELECT DB_ID();
输出表明我对第159页感兴趣(DBCC IND输出中的唯一行,PageType = 1).
现在,让我们看看一些选择页面细节,因为我们逐步完成了OP的场景.
DBCC PAGE(5,1,159,3);
UPDATE dbo.floob SET bar = 0 WHERE bar IS NULL; DBCC PAGE(5,3);
ALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL; DBCC PAGE(5,3);
现在,我没有得到所有答案,因为我不是一个内心深处的人.但很明显 – 虽然更新操作和NOT NULL约束的添加都无可否认地写入页面 – 后者以完全不同的方式这样做.它似乎实际上改变了记录的结构,而不仅仅是通过替换不可为空的列的可空列来改变位.为什么它必须这样做,我不太确定 – 我想这是一个很好的问题the storage engine team.我相信sql Server 2012可以更好地处理其中一些场景,FWIW – 但我还没有进行任何详尽的测试.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。