当您将列更改为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, 1, 159, 3);
ALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL;
DBCC PAGE(5, 1, 159, 3);
现在,我还没有所有答案,因为我不是一个内在深厚的人。但是很明显,尽管更新操作和添加的NOT NULL约束都无可否认地写入了页面,但后者却以完全不同的方式写入页面。通过将可为空的列换为不可为空的列,似乎实际上在改变记录的结构,而不仅仅是摆弄位。我不确定为什么要这么做,我想这对存储引擎团队来说是个好问题。我相信SQL Server 2012可以更好地处理其中一些情况,FWIW,但是我还没有做任何详尽的测试。
NOT NULL
默认列作为元数据操作的功能。另请参阅文档中的“将NOT NULL列添加为在线操作” 。