
ThoughtSpotでSCDType2ディメンションテーブルを直接結合する
AkimasaKajitaniです。
SCD Type2というデータ管理の手法をご存知でしょうか?
これは、「Slowly Changing Dimension Type 2」の略で、緩やかに(Slowly)変化する(Changing)属性(Dimension)を管理する手法のうちのタイプ2と呼ばれる方法です。例えば、顧客マスターの住所など、それほど頻繁に変化しない属性を管理するためのものです。
それほど変化が大きくない、ということなので、履歴を管理したとしてもそれほど時間の変化でレコード数が増えない、というのが前提となります。
なお、このSCDのタイプは7まであります。
- タイプ0:オリジナルを保持(初期値から変更しない)
- タイプ1:常に最新の値を保持(上書き)
- タイプ2:新規の行を追加(履歴を全て持つ)
- タイプ3:新規属性を追加
- タイプ4:最新値の保持と履歴テーブルで管理
- タイプ5:タイプ4と1の複合
- タイプ6:タイプ1、2、3の複合
- タイプ7:サロゲートキー(代理キー)とナチュラルキーの両用
ここでの本論はSCDの解説ではないため、詳細は別途確認願います。
いずれにしても、タイプ3くらいまでは1テーブルで実現可能な内容です。
なぜSCDType2を使うのか?
例えば、会員分析を行う際に、ある会員が最初(2020年)は岡山県に住んでいたとします。この人が今年(2026年)から東京に住んだとします。これにより、会員テーブルが最新値のみ保持するタイプのテーブルの場合(SCD Type1)、この人の住所が2026年に書き換わるわけですが、地域ごとの会員数推移を見る時に、この会員テーブルをそのまま使うと、2020年の岡山県の会員が一人減ってしまう、ということになります。
他のケースを考えると、部門ごとの売上分析で、部門再編による影響を正しく当時の組織名で見たい、という場合は部門マスターが書き換わって最新値のみになってしまうと、正しく分析ができない、ということになります。
つまり、過去の情報を当時の情報で扱いたい、という場合はSCD Type2以降のテーブルが必要になるということです。SCD Type0であれば、常に最初にテーブルに登録された属性での分析になりますし、SCD Type1であれば常に最新の属性での分析となります。
ThoughtSpotで直接SCD Type2のテーブルを扱う
SCD Type2のテーブルは、以下のようにそのデータ(行)の有効期限が書かれていることです。つまり、そのデータがいつ作成され、いつまで使われたか、ということです。現在使っているデータについては、現在使われていることを示すフラグを用いたりすることもあります。
例えば、今回はSCD Type2ディメンションテーブルとして以下のようなサンプルを扱います。
| user_id | user_name | rank_type | start_date | end_date | is_current |
|---|---|---|---|---|---|
| 101 | 田中 太郎 | Free | 2026/1/1 | 2026/4/30 | FALSE |
| 101 | 田中 太郎 | Premium | 2026/5/1 | 9999/12/31 | TRUE |
| 102 | 佐藤 玲子 | Gold | 2026/1/1 | 2026/3/14 | FALSE |
| 102 | 鈴木 玲子 | Gold | 2026/3/15 | 9999/12/31 | TRUE |
| 103 | 佐藤 健太 | Premium | 2026/1/1 | 2026/5/14 | FALSE |
| 103 | 佐藤 健太 | Silver | 2026/5/15 | 9999/12/31 | TRUE |
| 104 | 高橋 直樹 | Free | 2026/1/1 | 9999/12/31 | TRUE |
| 105 | 渡辺 憲一 | Premium | 2026/2/1 | 9999/12/31 | TRUE |
このテーブルは、start_dateとend_dateを使ってそのときどきの最新値を抽出することが可能です。
例えば、2026年3月末のデータを抜き出すSQL文は以下のようになります(DBはMySQLの場合)。
SELECT * FROM defaultdb.sdctype2_dim WHERE '2026-03-31' BETWEEN start_date AND end_date;
これで、以下のように出力されます(画面はMySQL Workbenchの画面)。

もしトランザクションテーブル側で、そのデータの発生日が入っていれば、その項目とstart_date、end_dateを比較することで、その時の属性を正確に抜き出すことができます。
ThoughtSpotでやってみる
それでは、ThoughtSpotにこのデータを取り込んで検索してみましょう。データ取り込みのところは省略します。
取り込みが完了したのち、例えば、2026年3月末時点の会員を見たいので、以下のように検索してみます。

ポイントは、start_dateは3月以前、end_dateは4月以降に設定します。これで、3月末時点で有効なデータが取得できます。

トランザクションテーブルと組み合わせる
次に、トランザクションテーブルと組み合わせてみましょう。
まず、普通にuser_id列を結合します。

次に、このモデルのTMLを開きます。

JOINの部分を探します。比較的上の方にあるのですぐにわかるかと思います。「joins」で検索するのも良いと思います(Ctrl+Fで検索用小窓が出てきます)。
その中のonの部分が結合条件となります。ここを編集します。

onの部分は以下のように記載があります。条件部分は以下の通りシングルクォテーションで囲む必要があります。
'on': '[scd_type2_dimension::user_id] = [SCDType2_Transaction::user_id]'
今回だとやりたいことを実現するためには、以下のような条件にします。
'on': ((([scd_type2_dimension::user_id] = [SCDType2_Transaction::user_id]) AND ([scd_type2_dimension::start_date] <= [SCDType2_Transaction::purchase_at])) AND ([scd_type2_dimension::end_date] >= [SCDType2_Transaction::purchase_at]))
カッコをしっかり適切につけないとうまく結果が出なかったりするのでご注意ください。ちなみに、上の記載内容は、最終的にTMLを編集後に再度開いたもので、ThoughtSpotによって自動的にカッコがつけられた結果のものです。
実際に入力したのは以下のような条件文です。
'on': (([scd_type2_dimension::user_id] = [SCDType2_Transaction::user_id]) AND ([scd_type2_dimension::start_date] <= [SCDType2_Transaction::purchase_at]) AND ([scd_type2_dimension::end_date] >= [SCDType2_Transaction::purchase_at]))
実際の画面では以下の青くハイライトされた部分のようになります。

あとは、「公開する」ボタンが右上にあるので、間違いなければ「公開する」ボタンをクリックします。

ステータスが「正常に公開されました」だと成功です。

ダイアログを閉じて、右上のバツボタンをクリックし、TMLエディタを閉じます。
あとは実際に検索するだけです。
ちなみに、このモデルの編集画面に移って結合を変えようとすると・・・

以下のようなメッセージが出てGUIでは編集できないのでご注意ください。

ただし、バージョン26.6時点ではベータではありますが非等価結合をUIで行うことができるようになっています。
モデルを検索する
それではモデルを検索してみましょう。

結果は以下のとおりですが、見てほしいのは「田中 太郎」さんの部分です。

5月からランクタイプがPremiumになっていたかと思いますが、実際に結合した結果も、その当時の会員ランク(Rank Type)が反映されています。
まとめ
- 非等価結合を用いてSCD Type2のディメンションとトランザクションデータを結合する方法をご紹介しました。
- 非等価結合が実装されているBIは少なく、ETLで事前に処理をして対応するケースが多いと思いますが、ThoughtSpotの場合は非等価結合がサポートされている希少なBIです。
運営会社