SQL Server : カラム追加時のnull許可の有無による実行速度の違いについて

既存のテーブルに新しいカラムを追加する際、not nullで追加すると、テーブルのレコード数によってはかなりの時間がかかることが経験上分かっていました。また、null許可で追加すると瞬時に終わることも経験上分かっています。

この時間差については[ドキュメント](https://docs.microsoft.com/ja-jp/sql/t-sql/statements/alter-table-transact-sql?view=sql-server-2017#adding-not-null-columns-as-an-online-operation)にも記載があります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/9bbebb3e-fae3-9325-2c90-015ee3d8f7ad.png)


このNULL許可の有無による、カラム追加時の実行速度の違いについて、トランザクションログに記録されるデータの違いを確認することで理解しようと試みました。

以下のサンプルテーブルを使って検証します。
検証環境はSQL Server 2014 Developer Edition で実施しました。

```sql
create table sampletable (val int)
insert into sampletable (val) values(1),(2),(3)
select * from sampletable 
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/68a47cbc-775e-32c7-cf56-abdea28b46d8.png)
サンプルテーブルには、3レコード入っています。

# トランザクションログの確認方法

```sql
select * from sys.fn_dblog(null, null)
```
トランザクションログは上記クエリを実行することで中身を確認できます。
このクエリだけを実行すると、検証で実行したクエリによるログ書き込み以外のデータも大量に取れてしまうため、checkpointを実行することで中身を（ほぼ）空の状態にします。

```sql
checkpoint
select * from sys.fn_dblog(null, null)
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/e5572000-851d-8a6f-5bd9-d761462fa694.png)

このように、3レコードだけが入っている状態になります。

参考：https://blog.engineer-memo.com/2011/04/05/sql-server-%E3%81%AE-delete-%E3%81%AE%E5%9F%BA%E6%9C%AC%E5%8B%95%E4%BD%9C%E3%82%92%E8%A6%8B%E3%81%A6%E3%81%BF%E3%82%8B/


#1. nullなカラムを追加

```sql
checkpoint
ALTER TABLE sampletable ADD column_int int NULL;
select * from sys.fn_dblog(null, null)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/94367aee-d48b-54a6-bef0-4934845a56bd.png)

AllocUnitName : 変更が行われたオブジェクト
Operation : 操作の種類

トランザクションログに書き込みが行われたことが分かります。
全てSQL Serverが内部で管理しているオブジェクトへの変更だけで、テーブル自体には変更が行われていないことが分かります。



#2. not nullなカラムを追加

```sql
checkpoint
ALTER TABLE sampletable ADD column_int int not NULL default(0);
select * from sys.fn_dblog(null, null)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/80730a3d-6452-a6dd-24b0-9648ed6f4e93.png)

トランザクションログに書き込まれるデータが変化したことは見て取れますが、not nullなカラムを追加しても、テーブル自体へは変更されていないようです。
[ドキュメント](https://docs.microsoft.com/ja-jp/sql/t-sql/statements/alter-table-transact-sql?view=sql-server-2017#adding-not-null-columns-as-an-online-operation)によると、以下のように、2012 Enterprise Edition以降であれば、not null列の追加でもオンライン操作となったようです。今回、検証環境が2014 Developer Editionだったためオンライン操作になっていました。トランザクションログの内容が変化したのは、規定値をテーブルのメタデータに格納する処理が増えたためと思われます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/5d4707b0-eba6-4860-0ae4-c475c9243be7.png)

NEWID()等を規定値とすると、2012 Enterprise Edition以降でもテーブルへの変更も実施されるとの記載もあったため、実験してみました。

```sql
checkpoint
ALTER TABLE sampletable ADD column_newid uniqueidentifier not NULL default(newid());
select * from sys.fn_dblog(null, null)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/25e612d1-53db-c892-dd4c-c40b7c89a825.png)
今度は、テーブルに対して行の修正操作が実施されたことが分かります。

バージョンによってはnot nullでのカラム追加はオフライン操作となり、オンライン操作をサポートしているバージョンでも追加するカラムの型やデフォルト値によってはオフライン操作となり、テーブルのレコードサイズが多いほど実行に時間がかかってしまうため、事前検証しておく必要がありそうです。

#3. メタデータの中身を確認してみる
```
select * from sys.sysschobjs order by created desc
select * from sys.syscolpars where id = 1271675578
```
カラム追加により変更されたメタデータは、[DAC](https://docs.microsoft.com/ja-jp/sql/database-engine/configure-windows/diagnostic-connection-for-database-administrators?view=sql-server-2017)で接続するとSELECTすることができます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/148310/e17f82b4-c2e1-f4fc-086e-8e38bea2ad55.png)


sys.sysschobjsは、制約やテーブルなどオブジェクトの管理テーブルのようです。
sys.syscolparsは、テーブルに紐づくカラムを管理しているテーブルのようでした。

#まとめ
nullまたはnot nullなカラムをテーブルに追加する際、トランザクションログに書き込まれるデータの違いから、各処理が完了するために必要な時間の違いを理解しました。

null許可 : メタデータのみなので瞬時書き込み（＝オンライン操作）
not null : 2012 Enterprise Edition以降で、[ランタイム定数](https://docs.microsoft.com/ja-jp/sql/t-sql/statements/alter-table-transact-sql?view=sql-server-2017#adding-not-null-columns-as-an-online-operation)を追加する場合は瞬時書き込み。それ以外はオフライン操作扱いのため、テーブルのレコード数が多ければ多いほど時間がかかる。事前検証必須。