3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SQL Server: 変更の追跡で変更された列を判断する

Last updated at Posted at 2021-08-12

変更の追跡機能を使うと、どの行が挿入、更新、削除されたかを追うことができます。

このとき、オプションを付ければ「どのカラムが更新されたか」まで追うことができるので、実験してみました。
※バージョンは2019 developer editionです。

create database change_tracking
go
use change_tracking

-- DBレベルで変更の追跡を有効化
alter database [change_tracking] set change_tracking = on
go

--テーブル作成
create table t1 (pk int identity(1,1) not null primary key, c1 int not null, c2 int not null)
create table t2 (pk int identity(1,1) not null primary key, c1 int not null, c2 int not null)

--変更の追跡の有効化(行の更新の追跡:ON)
alter table t1 enable change_tracking
with (track_columns_updated = on)
go

--変更の追跡の有効化(行の更新の追跡:OFF)
alter table t2 enable change_tracking
go

--設定を確認
select top 100 object_name(object_id) as table_name, * from sys.change_tracking_tables

サンプルクエリです。実行すると、行の更新の追跡をONにしてchange_trackingを有効化したt1と、change_trackingを有効化しただけのt2が作成されます。

システムテーブルのSELECT結果からも、t1だけが行の更新が追跡される設定になっていることが分かります。

image.png

次に、データをINSERTして、変更の追跡用テーブルをSELECTしてみます。

insert into t1 (c1, c2) values (1, 1), (2,2), (3,3), (4,4), (5,5)
insert into t2 (c1, c2) values (1, 1), (2,2), (3,3), (4,4), (5,5)
go

select
    *
from
     changetable(changes t1, 0) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
order by tc.commit_ts

select
    *
from
     changetable(changes t2, 0) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
order by tc.commit_ts
go

image.png
そちらも「I」=INSERTの履歴が格納されていることが分かります。
また、sys.dm_tran_commit_tableとJOINすることで、コミットした日時も取得することができます。

次に、データをDELETEしてみます。

delete from t1 where c1 = 1
delete from t2 where c1 = 1

select
    *
from
     changetable(changes t1, 0) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
order by tc.commit_ts

select
    *
from
     changetable(changes t2, 0) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
order by tc.commit_ts
go

image.png

ここまでは、結果は同じです。

最後に、データをUPDATEしてみます。

update t1 set c2 = 6 where c1 = 2
update t2 set c2 = 6 where c1 = 2

どのカラムが更新されたかは、CHANGE_TRACKING_IS_COLUMN_IN_MASK関数を使用します。

パラメータは

CHANGE_TRACKING_IS_COLUMN_IN_MASK ( column_id , change_columns )

となっています。(ドキュメントより引用)
column_idはカラムのID、change_columnsは、changetableデータのsys_change_columns列を使います。
これらを考慮したクエリは以下の通りです。

select
    change_tracking_is_column_in_mask(column_id , sys_change_columns) as is_updated
    ,*
from
     changetable(changes t1, 3) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
cross join sys.columns where object_id = object_id('t1')
order by tc.commit_ts

select
    change_tracking_is_column_in_mask(column_id , sys_change_columns) as is_updated
    ,*
from
     changetable(changes t2, 4) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
cross join sys.columns where object_id = object_id('t1')
order by tc.commit_ts

image.png

t1の方は、c2だけをUPDATEしているので、is_updateカラムがc2=1となっており、カラムの変更が追跡できていることが分かります。
t2の方は、is_updateカラムがすべて0になる想定だったのですが、何故かすべて1になっています。
原因調査のために試しに以下のクエリを実行したところ、1が返ってきました。

select change_tracking_is_column_in_mask(1, null)

image.png

これは想定外の挙動でした。t2では常にsys_change_columnsカラムはNULLになるため、正しい判定ができません。

この挙動を踏まえてクエリを以下のように書き換えてみました。

select
    (case when sys_change_columns is null then 0 else change_tracking_is_column_in_mask(column_id , sys_change_columns) end) as is_updated
    ,*
from
     changetable(changes t1, 3) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
cross join sys.columns where object_id = object_id('t1')
order by tc.commit_ts

select
    (case when sys_change_columns is null then 0 else change_tracking_is_column_in_mask(column_id , sys_change_columns) end) as is_updated
    ,*
from
     changetable(changes t2, 4) as c
join sys.dm_tran_commit_table tc on c.sys_change_version = tc.commit_ts
cross join sys.columns where object_id = object_id('t1')
order by tc.commit_ts

image.png

今度は想定した結果が得られました。

まとめ

変更の追跡を使って、どの列が更新されたかを確認する手順について実験してみました。

select change_tracking_is_column_in_mask(1, null)

というクエリが1を返すという挙動は覚えておくとよさそうです。

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?