1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Databricksメトリクスビューのウィンドウメジャーを試す

1
Posted at

はじめに

Databricksのメトリクスビューに、ウィンドウメジャー (Window Measure) という機能が追加されています (執筆時点では実験段階)。これは、移動平均、前日比、累積合計 (running total)、YTD (年初来累計) といった時系列分析特有の計算を、メトリクスビューのYAML定義の中で宣言的に書けるようにする機能です。

これまでメトリクスビューには、ある種の非対称性がありました。「売上」「注文数」のようなストック・フロー型のKPIは定義できる一方で、「7日ローリング売上」「YTD」「前日比成長率」といった時系列計算が絡むKPIは表現できず、結局SQLでウィンドウ関数を書くか、BIツール側の計算フィールドで再実装するしかありませんでした。つまり、時系列KPIだけがセマンティックレイヤーの外側に漏れていたわけです。

この漏れは具体的な失敗を生みます。ダッシュボードAでは「YTDは1月1日から」、ダッシュボードBでは「YTDは会計年度の4月1日から」と定義が分裂したり、Genieに「先月比で売上どう?」と聞いた答えが人間がSQLで書いた数字と合わなかったり、BIツールごとに移動平均のロジックを再実装する羽目になったりします。ウィンドウメジャーは、こうした時系列計算もセマンティックレイヤーのガバナンス下に置くためのものと捉えると、その存在意義が腹落ちしやすいと思います。

本記事では、公式ドキュメント メトリクス ビューの高度なテクニック のウィンドウメジャー部分を、実際に動かしながら解説します。

ウィンドウメジャーとは

通常のメジャー (SUM(o_totalprice) など) は、クエリ時のGROUP BYの粒度に応じて毎回ソースから集計し直されます。一方で「移動平均」や「累積合計」は、集計対象の行が現在の行を基点とした相対的なウィンドウで決まるため、通常のメジャーでは表現できません。

ウィンドウメジャーは、メジャー定義にwindowセクションを追加して、

  • どの軸に沿って並べるか (order)
  • どの範囲をウィンドウに含めるか (range)
  • ウィンドウの軸がクエリで指定されなかった場合の振る舞い (semiadditive)

を宣言することで、この相対的な集計を可能にします。

ウィンドウメジャーの定義要素

ウィンドウメジャーには以下の3つの必須要素があります。

order (順序)

ウィンドウの順序を決定するディメンション。多くの場合は日付ディメンションです。

range (範囲)

ウィンドウに含まれる行の範囲を定義します。

意味
current 現在の行と同じ順序値を持つ行のみ
cumulative 現在の行以前のすべての行 (累積)
trailing <N> <unit> 現在の行から指定単位数だけ前の行 (現在の行は含まない)
leading <N> <unit> 現在の行から指定単位数だけ先の行 (現在の行は含まない)
all すべての行

trailing 7 dayなどのように、単位はdaymonthyearなどの時間単位を指定できます。注意点として、trailing 3 months現在の月を含まない過去3か月です。

rangeがどの行をウィンドウに含めるかを、当日を基準に並べた表で示します。

range -3日 -2日 -1日 当日 +1日 +2日
current
trailing 3 day
leading 2 day
cumulative
all

trailingleadingは当日を含まない点、cumulativeは当日まで含む点に注意してください。

semiadditive (半加法属性)

クエリのGROUP BYにウィンドウのorder軸 (日付など) が含まれていない場合に、メジャーをどう集計するかを指定します。firstまたはlastを指定でき、それぞれウィンドウ内の最初または最後の値を返します。

この設定を理解するには「加法性 (additivity)」という考え方が役立ちます。メジャーはディメンションをまたいで素朴に合算できるかどうかで3種類に分かれます。

分類 ディメンションをまたいだ合算
加法的 (additive) すべての軸で合算OK 売上、注文数
半加法的 (semiadditive) 一部の軸では合算OK、他の軸では合算NG 口座残高、在庫数、契約者数
非加法的 (non-additive) どの軸でも素朴な合算は意味を成さない 平均価格、比率

代表例の「口座残高」を考えると、月曜残高100円と火曜残高120円を足して「220円」と言うのは意味不明 (時間軸ではNG) ですが、月曜時点で顧客A残高100円と顧客B残高300円を足して「合計残高400円」とは言えます (顧客軸ではOK)。このように軸によって振る舞いが変わるメジャーが半加法メジャーです。口座残高、在庫数、契約者数、社員数といった「ある時点でのスナップショット」を表すストック型メトリクスは、ほぼ全て半加法です。

semiadditive: lastは、この「時間軸では合算せず最新値を取る」を宣言するための設定です。これにより、利用者が日付ディメンション抜きでクエリしても、意味のある数字 (最新スナップショット) が返ります。

実例で理解する

ここからはTPC-Hサンプルデータ (samples.tpch.orders) を使って、典型的なパターンを順に見ていきます。

メトリクスビューはSQLでも作成できます。書式は以下です。

CREATE OR REPLACE VIEW <catalog>.<schema>.<view_name>
WITH METRICS LANGUAGE YAML AS $$
version: 1.1
source: samples.tpch.orders
...
$$

例1: 直近7日間のローリングカウント (trailing)

各日付の直前7日間に注文を行ったユニーク顧客数を計算するメトリクスです。日々の顧客エンゲージメントの長期トレンドを掴むのに使われます。

CREATE OR REPLACE VIEW takaakiyayoi_catalog.metric_views.orders_trailing
WITH METRICS LANGUAGE YAML AS $$
version: 1.1

source: samples.tpch.orders
filter: o_orderdate > DATE'1998-01-01'

dimensions:
  - name: date
    expr: o_orderdate

measures:
  - name: t7d_customers
    expr: COUNT(DISTINCT o_custkey)
    window:
      - order: date
        range: trailing 7 day
        semiadditive: last
$$

定義のポイントは以下のとおりです。

  • order: date で日付軸に沿って並べる
  • range: trailing 7 day で「現在の日を除く直前7日間」をウィンドウとする
  • semiadditive: last で、もしクエリ側で日付でグループ化していなくても、ウィンドウ内の最後の値を返す

クエリ側は通常のメジャーと同じです。

SELECT
  date,
  MEASURE(t7d_customers) AS trailing_7d_customers
FROM takaakiyayoi_catalog.metric_views.orders_trailing
WHERE date BETWEEN DATE'1998-01-01' AND DATE'1998-08-01'
GROUP BY ALL
ORDER BY date

日次のユニーク顧客数 (約3千) と直近7日のローリング値 (約2万1千) はスケールが大きく異なるため、デュアル軸で可視化すると比較しやすくなります。重複の少ない顧客プールに対するtrailing 7 dayは、おおよそ7日合算に近い水準まで膨らみます。

なお、TPC-Hはベンチマーク用に人工生成された一様なデータのため、ローリング値はほぼフラットな線になります。現実のEC・小売データであれば、週末スパイク、月末セール、季節性などによってローリング値に動きが出てきます。

newplot (1).png

例2: 前日比成長率 (current vs trailing 1)

「今日の売上」と「昨日の売上」を別々のメジャーとして定義し、その差分を割合で出すパターンです。

CREATE OR REPLACE VIEW takaakiyayoi_catalog.metric_views.orders_dod
WITH METRICS LANGUAGE YAML AS $$
version: 1.1

source: samples.tpch.orders
filter: o_orderdate > DATE'1998-01-01'

dimensions:
  - name: date
    expr: o_orderdate

measures:
  - name: previous_day_sales
    expr: SUM(o_totalprice)
    window:
      - order: date
        range: trailing 1 day
        semiadditive: last

  - name: current_day_sales
    expr: SUM(o_totalprice)
    window:
      - order: date
        range: current
        semiadditive: last

  - name: day_over_day_growth
    expr: (MEASURE(current_day_sales) - MEASURE(previous_day_sales)) / MEASURE(previous_day_sales) * 100
$$

ここで使われているMEASURE()は、メジャーを別のメジャーから参照するための関数です。これにより構成可能性 (composability) が成立し、ウィンドウメジャー同士を組み合わせて派生メトリクスを定義できます。

newplot (2).png

例3: 累積合計 (cumulative)

データセットの開始時点から各日付までの累計売上を計算します。

CREATE OR REPLACE VIEW takaakiyayoi_catalog.metric_views.orders_cumulative
WITH METRICS LANGUAGE YAML AS $$
version: 1.1

source: samples.tpch.orders
filter: o_orderdate > DATE'1998-01-01'

dimensions:
  - name: date
    expr: o_orderdate

measures:
  - name: running_total_sales
    expr: SUM(o_totalprice)
    window:
      - order: date
        range: cumulative
        semiadditive: last
$$

range: cumulativeは「データセットの最初の行から現在の行まで」を意味します。

クエリ側で日付ディメンションを使わずに集計した場合、semiadditive: lastの指定が効いて「最新時点での累積値」が返ります。これは「現時点までの累積合計を1つの数字で知りたい」というスコアカード的なユースケースに向いています。

newplot (3).png

例4: 期間累計 (YTD)

毎年1月1日にリセットされる年初来累計 (Year To Date) は、ウィンドウを2つ定義することで実現できます。

CREATE OR REPLACE VIEW takaakiyayoi_catalog.metric_views.orders_ytd
WITH METRICS LANGUAGE YAML AS $$
version: 1.1

source: samples.tpch.orders
filter: o_orderdate > DATE'1997-01-01'

dimensions:
  - name: date
    expr: o_orderdate
  - name: year
    expr: DATE_TRUNC('year', o_orderdate)

measures:
  - name: ytd_sales
    expr: SUM(o_totalprice)
    window:
      - order: date
        range: cumulative
        semiadditive: last
      - order: year
        range: current
        semiadditive: last
$$

2つ目のウィンドウorder: year, range: currentが、累積範囲を「当年内のみ」に制限します。これにより、年の切り替わりで自動的にカウンタがリセットされる、本来のYTDの振る舞いになります。

newplot (4).png

例5: 半加法メジャー (口座残高など)

range: current, semiadditive: lastの組み合わせは、ストック型メトリクス (口座残高、在庫数、契約者数など) に向いています。これらは

  • 日付軸では合算してはいけない (月曜と火曜の残高は足せない)
  • 顧客軸では合算してよい (同日の全顧客残高は合算してよい)

という性質を持つため、半加法と呼ばれます。

CREATE OR REPLACE VIEW takaakiyayoi_catalog.metric_views.account_balance
WITH METRICS LANGUAGE YAML AS $$
version: 1.1

source: <your_balance_table>

dimensions:
  - name: date
    expr: date
  - name: customer
    expr: customer_id

measures:
  - name: semiadditive_balance
    expr: SUM(balance)
    window:
      - order: date
        range: current
        semiadditive: last
$$

semiadditive: lastがあるおかげで、複数日にまたがってクエリしても最新日の残高が返り、顧客横断ではちゃんと合算されます。

実際に以下の3パターンを実行すると、半加法の挙動が数字で確認できます。

(a) 日付別の残高 - 日付でグループ化したクエリ。各日付の全顧客合計が返ります。

Screenshot 2026-05-13 at 17.50.01.png

(b) 日付なしクエリ - 日付ディメンションを使わない集計。semiadditive: lastの効果で、複数日の合算ではなく最新日 (2026-01-03) の合計 110 + 280 + 90 = 480 が返ります。

Screenshot 2026-05-13 at 17.49.38.png

(c) 顧客別の残高 - 顧客でグループ化したクエリ。各顧客の最新日の残高が返ります。

Screenshot 2026-05-13 at 17.50.33.png

ウィンドウメジャーのクエリ

ウィンドウメジャーも、通常のメジャーと同じくMEASURE()関数で参照します。

SELECT
  DATE_TRUNC('month', date) AS month,
  MEASURE(t7d_customers) AS m
FROM takaakiyayoi_catalog.metric_views.orders_trailing
WHERE date >= DATE'1998-01-01'
GROUP BY ALL

ポイントは、利用者側はウィンドウメジャーかどうかを意識せずに使えることです。「過去7日のユニーク顧客数」というビジネス概念がメトリクスビュー側に閉じ込められているので、ダッシュボードを作る人もGenieに質問する人も、定義の中身を再実装する必要がありません。

まとめ

ウィンドウメジャーを使うと、これまでセマンティックレイヤーから漏れがちだった時系列計算もメトリクスビューの中に取り込めるようになります。具体的なユースケースとしては以下のようなものがあります。

  • 移動平均 (trailing N days)
  • 前日比、前週比 (currenttrailing 1 の組み合わせ)
  • 累積合計 (cumulative)
  • YTD、QTD、MTD (cumulative + year/quarter/month current のネスト)
  • 半加法メジャー (current + semiadditive: last)

実験段階の機能ではありますが、組織横断でKPIを統一したい場面ではかなり強力です。ぜひお手元のDatabricks環境で試してみてください。

関連リンク

はじめてのDatabricks

はじめてのDatabricks

Databricks無料トライアル

Databricks無料トライアル

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?