我们有一个包含2.3B行的表.我们想将列从NOT NULL更改为NULL.该列包含在一个索引中(不是聚簇索引或PK索引).数据类型没有变化(它是INT).只是可空性.声明如下:
Alter Table dbo.Workflow Alter Column LineId Int NULL
在我们停止之前操作需要超过10(我们甚至还没有让它运行完成,因为它是一个阻塞操作并且耗时太长).我们可能会将表复制到开发服务器测试实际需要多长时间.但是,我很好奇是否有人知道在从NOT NULL转换为NULL时sql Server正在做什么?此外,受影响的索引是否需要重建?生成的查询计划不会指示发生了什么.
有问题的表是聚集的(不是堆).
解决方法
正如@Souplex在评论中所提到的,一个可能的解释可能是该列是否是它参与的非聚集索引中的第一个NULL-able列.
对于以下设置
CREATE TABLE Foo ( A UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,B CHAR(1) NOT NULL DEFAULT 'B' ) CREATE NONCLUSTERED INDEX ix ON Foo(B); INSERT INTO Foo (B) SELECT TOP 100000 'B' FROM master..spt_values v1,master..spt_values v2
sys.dm_db_index_physical_stats显示非聚集索引ix有248个叶子页面和一个根页面.
索引叶子页面中的典型行看起来像
并在根页面
然后跑……
CHECKPOINT; GO ALTER TABLE Foo ALTER COLUMN B CHAR(1) NULL; SELECT Operation,Context,ROUND(SUM([Log Record Length]) / 1024.0,1) AS [Log KB],COUNT(*) as [OperationCount] FROM sys.fn_dblog(NULL,NULL) WHERE AllocUnitName = 'dbo.Foo.ix' GROUP BY Operation,Context
回
+-----------------+--------------------+-------------+----------------+ | Operation | Context | Log KB | OperationCount | +-----------------+--------------------+-------------+----------------+ | LOP_SET_BITS | LCX_GAM | 4.200000 | 69 | | LOP_FORMAT_PAGE | LCX_IAM | 0.100000 | 1 | | LOP_SET_BITS | LCX_IAM | 4.200000 | 69 | | LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | 8.700000 | 3 | | LOP_FORMAT_PAGE | LCX_INDEX_LEAF | 2296.200000 | 285 | | LOP_MODIFY_ROW | LCX_PFS | 16.300000 | 189 | +-----------------+--------------------+-------------+----------------+
再次检查索引叶子,行现在看起来像
和上一级页面中的行如下.
每行都已更新,现在包含两个字节用于列计数以及另一个字节用于NULL_BITMAP.
由于额外的行宽,非聚集索引现在有285个叶子页面,现在有两个中间级别页面和根页面.
的执行计划
ALTER TABLE Foo ALTER COLUMN B CHAR(1) NULL;
看起来如下
这将创建索引的全新副本,而不是更新现有索引并需要拆分页面.