DB Stories

DBに関する過去、現在、未来の話題をプロフェッショナルの視点で紹介

スタースキーマとキューブ(9)

緩やかに変化するディメンション(Slowly Changing Dimensions)のType2変更について解説を続けます。

Type2変更

データソースとなる業務システムで行われる多くの変更はスタースキーマにおいてType2変更として扱います。Type2変更はファクトの変更履歴を保持する方法です。ファクトテーブルにおいて変更前のデータは既にあるレコードで、変更後のデータは新しいレコードとして扱います。

ディメンションへの新規レコードの登録

2つ目の方法は新しいレコードをディメンションテーブルに追加(insert)する方法です。既にあるレコードに対しては変更しません。このType2変更は、変更前のデータは既存のレコードに保持していることになるし、変更後のデータは新しいレコードとして扱います。

前回の紹介した例で考えてみます。Sue Jhonsonは初めの住所は東京都ですが、のちに転居に伴い大阪府に変更されています。そのことから、東京都に住んでいたときの注文(Order)と大阪府に転居した後の注文が存在するはずです。Type1変更を行った場合、転居前の注文を参照することが出来なくなるという副作用が出てくることになります。全ての注文が転居後の大阪府に関連されることになります。

次の図はSueの住所についてのType2変更を行った場合の例を示します。「更新前」において、ティメンションテーブルにはcustomer_id(9900011:Sue)は東京都に住んでいることを示しています。このレコードはサロゲートキーとしてcustomer_key(1449)となっています。orderファクトテーブルはこのキーを持ち、Sueの注文情報を持っています。

f:id:good-value:20170313180538p:plain

「更新後」についてはSueの住所が変更した時の状況を示しています。ディメンションテーブルにおいてSueの既に存在するレコード(customer_key=1499)については何も変更していないので、Sueが東京都に以前住んでいたことが分かります。新しいレコードをcustomer_key=2507として追加しています。この新しいレコードはSueが現在大阪府に住んでいる事を示しています。

Type2はSueについての情報のバージョンをディメンションテーブルに保持していることになります。今までは1行でしたが、今回2行になっています。このデータのバージョンは、ディメンションテーブルにおいてナチュラルキーである(cusomer_id)ではなくサロゲートキー(customer_key)を保持していることで可能となります。

ファクトの変更履歴を保存する

ディメンションテーブルに複数のバージョン(レコード)を保持することで、既に存在するファクトデータの分析を正しく行えることになります。過去のファクトデータは既にあるディメンションのレコードで行い、新しいファクトデータはディメンションなら新しいレコードで実施します。つまり履歴を保持していることになります。

Type2変更がどのように履歴を保持するかというのを示しているのが図の「更新後」に示されています。Type変更が行われた後、Sueが転居する前のorderは古いバージョンのCustomerディメンションに関連付けされます。customerテーブルのcusomer_key(1499)はcustomer_id(9900011)のSue Johnsonが東京都に住んでいることを示しています。ファクトテーブルに追加された新しい行は転居後の情報を示しています。この新しい行はサロゲートキーcustomer_key(2507)を利用していて、Sueが大阪府に住んでいるという新しいバージョンのデータに関連しています。

Type2変更が行われるとき、すべてのディメンションテーブルが変更されるわけではありません。例えば、Sueが大阪に転居した後も誕生日は1971年2月3日というのは変わりません。「誕生日行われた注文」を分析する場合は、Sueのすべてのデータ(転居前も後も)を利用することが可能です。「Sueの行った注文」を分析する場合は、customer_id(9900011)を利用することになります。

ディメンションの履歴は一部のみ保持される

Type2変更は結果として一つのナチュラルキーに対して複数のレコードを持つことになります。このことがファクトを履歴を加味した分析を可能にしますが、別の混乱を生じさせる場合があります。「ディメンションテーブルに重複した値が存在している」ということです。例えば、Sue Johnsonの住所が変更された後、ディメンションテーブルにはcustomer_id(9900011)のレコードが2つ存在しています。もし、customer_id(9900011)についての名前を取得しようとした場合、2行結果が返ることになります。この副作用については値の一覧取得用のクエリ(Browse query)の際にdistinct句を利用することで対応します。 Browwse Queryについては分析システムにおける多次元データモデル(3)を参照してください。 ナチュラルキーごとに最新行を示すフラグを保持する方法もありますが、これについて解説は後程行います。

Type2変更はファクトの履歴分析を行うためのものですが、ディメンションそのものの履歴を保持するわけではありません。ディメンションテーブルにおいてあるナチュラルキーは複数行持つことになりますが、それぞれのレコードがいつの時点で正しいのかということを知ることができません。この情報は全てファクトテーブルに存在しています。

例えば、Sueの住所の変更があった後、ディメンションテーブルのSueのレコードは2行存在することになるが、Sueの変更がいつ会ったのかは知ることができません。「2008年1月1日にどこに住んでいたのか?」という質問に答えることはできません。運用良く2008年1月1日に注文があった場合、ファクトテーブルにデータが登録されるタイミングでSueのレコードと関連付けが行われるため質問に答えることができますが、注文がない場合その時点でのSueの情報を知ることはできません。

この種の問題はSueのディメンション情報にタイムスタンプ列を定義することで解決可能です。このテクニックにより、ディメンションテーブルはファクトの履歴とディメンションの履歴を保持することを可能にします。別のファクトテーブルを作成してSueの変更履歴を持つ方法がります。この詳細な解説はまた後で行います。

(次回、ここまで紹介したディメンション変更の選択の考え方について解説します)