sql-server – 将列从NOT NULL更改为NULL – 引擎盖下发生了什么?

前端之家收集整理的这篇文章主要介绍了sql-server – 将列从NOT NULL更改为NULL – 引擎盖下发生了什么?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我们有一个包含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;

看起来如下

这将创建索引的全新副本,而不是更新现有索引并需要拆分页面.

猜你在找的MsSQL相关文章