2.3B行のテーブルがあります。列をNOT NULLからNULLに変更します。列は1つのインデックスに含まれます(クラスター化インデックスまたはPKインデックスではありません)。データ型は変更されていません(INTです)。ヌル可能性のみ。ステートメントは次のとおりです。
Alter Table dbo.Workflow Alter Column LineId Int NULL
操作を停止するまでに10を超える時間がかかります(これは、ブロッキング操作であり、時間がかかりすぎていたため、まだ完全に実行することもできていません)。おそらく、実際にかかる時間をテストするために、テーブルを開発サーバーにコピーします。しかし、NOT NULLからNULLに変換するときにSQL Serverが内部で何をしているのか誰かが知っているかどうか知りたいのですが。また、影響を受けるインデックスを再構築する必要がありますか?生成されたクエリプランは、何が起こっているかを示していません。
問題のテーブルはクラスター化されています(ヒープではありません)。
コメントで@Souplexが言及したように、考えられる理由の1つは、この列が、それが参加する非クラスター化インデックスの最初の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 |
+-----------------+--------------------+-------------+----------------+
インデックスリーフを再度チェックすると、行は次のようになります。
上位レベルのページの行は以下のとおりです。
各行が更新され、列カウントの2バイトとNULL_BITMAPの別のバイトが含まれるようになりました。
追加の行幅により、非クラスター化インデックスには285のリーフページがあり、ルートページと共に2つの中間レベルのページがあります。
の実行計画
ALTER TABLE Foo ALTER COLUMN B CHAR(1) NULL;
次のようになります
これにより、既存のインデックスを更新してページを分割する必要がなくなり、インデックスの新しいコピーが作成されます。
メタデータを更新するだけでなく、非クラスター化インデックスを確実に再作成します。これはSQL 2014でテストされており、実際の運用システムではテストしないでください。
_CREATE TABLE [z](
[a] [int] IDENTITY(1,1) NOT NULL,
[b] [int] NOT NULL,
CONSTRAINT [c_a] PRIMARY KEY CLUSTERED ([a] ASC))
go
CREATE NONCLUSTERED INDEX [nc_b] on z (b asc)
GO
insert into z (b)
values (1);
_
そして今、楽しい部分のために:
_DBCC IND (0, z, -1)
_
これにより、テーブルと非クラスター化インデックスが格納されているデータベースページが得られます。
PagePID
を検索します。ここで、IndexID
は2、PageType
は2です。次の手順を実行します。
_DBCC TRACEON(3604) --are you sure that you are allowed to do this?
_
その後:
_dbcc page (0, 1, PagePID, 3) with tableresults
_
ヘッダーにnullビットマップがあることに注意してください。
今やりましょう:
_alter table z alter Column b int null;
_
本当に焦っている場合は、もう一度_dbcc page
_コマンドを実行してみてください。ただし、失敗するので、DBCC IND (0, z, -1)
を使用して割り当てをもう一度確認してみましょう。ページは魔法のように移動します。
したがって、メタデータを更新する必要があり、後でインデックスを再構築する必要がないため、列のNULL可能性を変更すると、その列をカバーする非クラスター化インデックスのストレージに影響します。
SQL Server 2016以降、多くの_ALTER TABLE ... ALTER COLUMN ...
_操作をONLINE
実行できますが、
- 列を_
NOT NULL
_からNULL
に変更することは、変更された列が非クラスター化インデックスによって参照されている場合、オンライン操作としてサポートされません。