SCD Type2にて既存行より有効開始日が古いデータが到着した場合の取り扱い

SCD Type2にて既存行より有効開始日が古い(または同じ日付の)データが到着した場合の取り扱いを考えてみました。


前提:この方式を採用
■Design Tip #107 Using the SQL MERGE Statement for Slowly Changing Dimension Processing
http://www.kimballgroup.com/html/08dt/KU107_UsingSQL_MERGESlowlyChangingDimension.pdf


既存行(Target)
ビジネスキー 有効開始日 有効終了日 最新フラグ
AAA     2011/01/01 2011/03/31 N
AAA     2011/04/01 2011/05/24 N
AAA     2011/05/25 9999/12/31(とかNULLとか) Y


到着したデータ(Source)
ビジネスキー 有効開始日
AAA     2011/02/01
※有効開始日の決定方法は何でもよいが(基幹側からの指定日(未来日付を含む)、処理日、処理日の前日、処理みなし日、...)、とにかく特定の日付が有効開始日として指定されたとする。


案A:無視する
上記PDFの、Type2の9行目: WHEN MATCHED AND CM.Current_Flag = 'y' を変更する。
→WHEN MATCHED ND CM.Current_Flag = 'y' AND Source.有効開始日 > Target.有効開始日


案B:拒否する
事前に(Source)と(Target)の最新有効行を比較して、RAISERRORですね...


案C:許容する
上記PDFの、Type2の9行目: WHEN MATCHED AND CM.Current_Flag = 'y' を変更する。
→WHEN MATCHED AND Source.有効終了日 > Target.有効終了日(有効開始日の前日)
上記PDFの、Type2の14行目:WHERE MERGE_OUT.Action_Out = 'UPDATE'; を変更する。
→WHERE MERGE_OUT.Action_Out = 'UPDATE' AND Source.有効開始日 < Target.有効開始日


ビジネスキー 有効開始日 有効終了日
AAA     2011/01/01 2011/01/31 ←有効終了日が更新、有効期間の短縮化
AAA     2011/04/01 2011/01/31 ←有効終了日が更新、無効化、削除してよい
AAA     2011/05/25 2011/01/31 ←有効終了日が更新、無効化、削除してよい
AAA     2011/02/01 9999/12/31(とかNULLとか)

※有効期間に変化があるため、影響のあるファクト側のサロゲートキーのルックアップの再処理が必要です。


うーん、めんどくさい。

SCD Type2 設計の観点のメモ

SCD Type2 設計の観点のメモです。


・変化する時間の粒度
ディメンションデータの「変化する時間の粒度」は、ファクトと一致するはず。例えば、ファクト行を特徴付ける時間列がdate型(例:伝票日付)なら、ディメンションの有効範囲もdate型で日単位のはず。同様に、ファクトがdatetime粒度であれば同じ粒度のディメンションが用意されるはず。同様に、month粒度やyear粒度などがあってもよい。
→SCD Type2を行う時間粒度によって、ディメンションは「date粒度」「month粒度」などに区別されるべき。
→変化粒度が「month粒度」や「year粒度」となるマスタデータとしては勘定科目などが挙げられる。基幹システム側での管理レベルが重要。
→変化粒度が「datetime粒度」になる場合はSlowlyの範疇ではなくなる?(→RCD、ファクトに入れる)いや、Slowlyでたまたま区切りが秒単位ということもある。


・Type1の属性とType2の属性は同居する
別にType0でも3でも4でも構わないのですが、属性ごとにTypeを区別しましょう、という普通の話です。


・late arriving fact
これはファクト側のサロゲートキーのルックアップ処理で考慮すべきことですが...


スノーフレークスキーマのType2
そのままスノーフレークスキーマで実装するのが楽。スタースキーマに変換するアイデアとしては...FULL OUTER JOIN して、有効期間が重複している行を残す(これの繰り返し)。最後にMEARGE+MEARGE OUT を行う。

SCD Type2の効率的な実装方式

SSIST-SQLでSCD Type2を大量に実装する必要があって、効率的な実装方式を考えたり探したりした結果のメモです。

SSISの既定のSCDコンポーネントに少し不満があって、SSISのデータフローコンポーネントで組み上げるか、T-SQLのMEARGEで...効率的なのはどちらかな...と、脳内で考えていたのですが google ったら結論出てました。


まずこちら。3種類の代替手段の紹介です。よくまとまってます。

■Alternatives to SSIS SCD Wizard Component
http://bennyaustin.wordpress.com/2010/05/29/alternatives-to-ssis-scd-wizard-component/


私が採用する実装方式はこちら。Kimball Group はさすがですね!

■Design Tip #107 Using the SQL MERGE Statement for Slowly Changing Dimension Processing
http://www.kimballgroup.com/html/08dt/KU107_UsingSQL_MERGESlowlyChangingDimension.pdf


(追記)
MSDN Blogs > SSIS Team Blog > Handling Slowly Changing Dimensions in SSIS
http://blogs.msdn.com/b/mattm/archive/2009/11/13/handling-slowly-changing-dimensions-in-ssis.aspx

DATE_CORRELATION_OPTIMIZATION オプションっていうものがありましたね

過去に作成した資料を読んでいて DATE_CORRELATION_OPTIMIZATION オプションを思い出したのでまた忘れたりしないようにメモしておきます(たぶん忘れます)。


■相関した datetime 列にアクセスするクエリの最適化
http://msdn.microsoft.com/ja-jp/library/ms177416.aspx

金額規模ディメンションとリードタイムメジャーの実装

まず、金額規模ディメンションの実装方式のアイデア


・ディメンション属性の離散化プロパティで実装(再評価の必要なし)
・データソースビューの名前付きクエリでCASE句などで実装(メジャーグループの処理で再評価される)、これが良さそう(性能を考慮すると基準値をハードコーディングすることになりますけどね)
サロゲートキー変換時
・データウェアハウス配置時


似たような話で、リードタイムメジャーの実装方式のアイデア


GETDATE()などを使わない場合
・テーブルにカラムを持つ
・テーブルの計算列で実装、これが良さそう(物理保持するかしないか選べる)
・データソースビューの名前付き計算で実装


GETDATE()などを使う場合
・データソースビューの名前付き計算で実装(メジャーグループの処理で再評価される)、これ以外は駄目そう


再評価のときの面倒具合を考慮して評価するタイミングを決めることですね...

サロゲートキーのルックアップの実装

以下の方式はある種の前提におけるものですが、基本的な実装方式としてお勧めできます。
記憶から書き出していますので記述漏れがあるかもしれませんが大部分はカバーできていると思います。


・ファクトテーブル−ディメンションテーブル間では参照整合性制約を設定しない。
サロゲートキーのルックアップにはSSISの参照変換コンポーネントを使う。
SSIS参照変換コンポーネントの「詳細設定」の「カスタムクエリ」では以下のWHERE句を使用する。
 WHERE [ビジネスキー] = ? AND [有効開始日] <= ? AND [有効終了日] >= ?
 (※性能を重視する場合は別の選択肢を採りますので誤解のないようにお願いします)
・「カスタムクエリ」を使う都合上、キャッシュモードは「部分キャッシュ」となる
SSIS参照変換コンポーネントの「全般」の「エントリが一致しない行の...」は「エラーを無視する」を使用する。
・変換先コンポーネントでは「NULLを保持する」をOFF(既定値)にする。
・変換先コンポーネントでの挿入先テーブル(ステージング)では、サロゲートキー列には初期値(ゼロ)を設定しておく。
・ディメンションテーブルではサロゲートキー値ゼロのデータ(UnknownMember)を用意しておく。
・ディメンションテーブルのサロゲートキー列はIDENTITY(1, 1)とする(ゼロは生成されません)。


せっかく用意されているSSASのUnknownMemberの仕組みを使わずに自作する!のです。


(追記)SQL実行タスク、LEFT OUTER JOIN 、ISNULL([サロゲートキー], 0) でも良いですね。→これだとSSIS使わないですね...


(追記)高速化のアイデア
・キャッシュモードを「フルキャッシュ」にする
・キャッシュ接続マネージャを使う
・キャッシュ変換コンポーネントを使用して事前にキャッシュファイル(caw)の内容を整える
・「カスタムクエリ」は使えない(これが理由というわけでもないが)ので、キャッシュ変換コンポーネントでキャッシュファイルに与えるデータを「該当する日付」や「最新の日付」でフィルタしておく
・参照変換コンポーネントではビジネスキーの一致のみで判断できる
・キャッシュファイルの配置先とかセキュリティとか面倒な感じ


(追記)Merge Join + Conditional Split の方が高速。けどプロパティ設定面倒そう。
MSDN Blogs > SSIS Team Blog > Lookup Pattern: Range Lookups
http://blogs.msdn.com/b/mattm/archive/2008/11/25/lookup-pattern-range-lookups.aspx

計算メジャーでの Empty の考慮

計算メジャーの設定で、ゼロ除算だけ避けて単純に


Iif([Measures].[分母] = 0, "n/a", [Measures][分子]/[Measures].[分母])
("n/a" のところを "" 空文字 にしたり...)


としたところ、[分子]や[分母]にそもそも値が無いセルに "n/a" が表示されてしまって駄目な感じになるので、こんな感じにしました、といういまさらのメモです。


Case
When IsEmpty([Measures].[分子]) Or IsEmpty([Measures].[分母]) Then Null
When [Measures].[分母] = 0 Then "n/a"
Else [Measures].[分子]/[Measures].[分母]
End


より良い記述を求む。