2
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?

PR: データブリックス・ジャパン株式会社
MLflowで実践するLLMOps

Databricks の Metric View は直接 JOIN できない ― Genie が生成する SQL が「揺れる」理由と対処

2
Posted at

TL;DR

  • Databricks の Metric View は、クエリ時に他のテーブルとも他の Metric View とも「直接 JOIN」できない。やると METRIC_VIEW_JOIN_NOT_SUPPORTED エラーになる。
  • 正解は 各 Metric View を CTE(WITH 句)で MEASURE() + GROUP BY して集計してから、CTE 同士を JOIN すること。
  • Genie エージェントで複数 Metric View をまたぐ質問をすると、生成 SQL が「CTE 化する正しい形」と「直接 JOIN する誤った形」に揺れることがある。これは Genie の SQL 生成が非決定的なため。
  • ナレッジストアの JOIN 定義は「実行時の結合」ではなく「生成のヒント」 にすぎない。安定化には Example SQL(CTE 形の見本)を登録して生成を寄せるのが最も効く。

この記事の SQL は samples.tpch(Databricks に標準で付属するサンプル)でそのまま試せます。

Metric View とは(前提)

Metric View は、KPI(メジャー)と分析軸(ディメンション)を YAML で一元定義する Unity Catalog オブジェクトです。BI ツールや Genie から「売上」「注文数」といったビジネス指標を一貫した定義で参照でき、集計は MEASURE() 関数を通して評価します。

まず samples.tpch から Metric View を 2 つ作ります。

CREATE SCHEMA IF NOT EXISTS main.mv_demo;

-- 注文側の Metric View
CREATE OR REPLACE VIEW main.mv_demo.orders_mv WITH METRICS
LANGUAGE YAML
AS $$
version: 0.1
source: samples.tpch.orders
dimensions:
  - name: custkey
    expr: o_custkey
measures:
  - name: order_count
    expr: COUNT(1)
  - name: total_revenue
    expr: SUM(o_totalprice)
$$;

-- 顧客側の Metric View
CREATE OR REPLACE VIEW main.mv_demo.customer_mv WITH METRICS
LANGUAGE YAML
AS $$
version: 0.1
source: samples.tpch.customer
dimensions:
  - name: custkey
    expr: c_custkey
measures:
  - name: total_acctbal
    expr: SUM(c_acctbal)
$$;

単体で参照するときは、メジャーを MEASURE() で包みます。

SELECT custkey, MEASURE(total_revenue) AS revenue
FROM main.mv_demo.orders_mv
GROUP BY custkey;

本題:Metric View は直接 JOIN できない

顧客ごとに「売上合計」と「口座残高合計」を並べたい、という素朴な要求を考えます。2 つの Metric View を custkey で JOIN すれば良さそうに見えます。

-- ❌ これはエラーになる
SELECT o.custkey, MEASURE(o.total_revenue) AS rev, MEASURE(c.total_acctbal) AS bal
FROM main.mv_demo.orders_mv o
JOIN main.mv_demo.customer_mv c ON o.custkey = c.custkey
GROUP BY o.custkey;

結果:

[METRIC_VIEW_JOIN_NOT_SUPPORTED] The metric view is not allowed to use joins.

Metric View と通常テーブルの直接 JOIN も、同じエラーになります。

-- ❌ これもエラー(相手が通常テーブルでも同じ)
SELECT o.custkey, MEASURE(o.total_revenue) AS rev, c.c_name
FROM main.mv_demo.orders_mv o
JOIN samples.tpch.customer c ON o.custkey = c.c_custkey
GROUP BY o.custkey, c.c_name;

これは公式ドキュメントにも明記された仕様です。バグではありません。

メトリックビューは、クエリ時に他のテーブルと直接結合できません。メトリックビューを別のテーブルと結合するには、メトリックビューのクエリを CTE でラップし、その CTE の結果を結合します。

正解:CTE で集計してから JOIN する

各 Metric View を WITH 句(CTE)の中で先に集計し、「ただの集計済み結果」に変えてから、CTE 同士を JOIN します。

-- ✅ CTE 化してから JOIN する
WITH orders AS (
  SELECT custkey,
         MEASURE(total_revenue) AS revenue,
         MEASURE(order_count)   AS order_count
  FROM main.mv_demo.orders_mv
  GROUP BY custkey
),
cust AS (
  SELECT custkey,
         MEASURE(total_acctbal) AS acctbal
  FROM main.mv_demo.customer_mv
  GROUP BY custkey
)
SELECT o.custkey, o.revenue, o.order_count, c.acctbal
FROM orders o
JOIN cust c ON o.custkey = c.custkey
ORDER BY o.revenue DESC
LIMIT 5;

ポイントは 2 つです。

  1. メジャーは CTE の中で MEASURE() を使って評価し、GROUP BY で粒度を確定させる。
  2. JOIN キーはディメンションとして各 CTE に残し、その列で CTE 同士を JOIN する。(メジャーではなく、CTE に残した次元で結合する)

CTE 化した時点で中身は通常の集計結果になっているため、METRIC_VIEW_JOIN_NOT_SUPPORTED の制約に触れずに結合できます。

Metric View は、別の Metric View を source にして積み重ねる「合成(composability)」もできます。その際、派生側で元のメジャーを参照するときは MEASURE() で包みます(例: expr: MEASURE(total_revenue))。

Genie で起きる「揺れ」

ここからが本題です。Genie エージェントに複数の Metric View を登録し、「顧客ごとに売上合計と口座残高合計を並べて」と自然言語で聞くと、生成される SQL が

  • CTE 化してから JOIN する正しい形 → 成功
  • Metric View 同士を直接 JOIN する誤った形 → METRIC_VIEW_JOIN_NOT_SUPPORTED で失敗

の 2 通りに揺れることがあります。

これは Genie が壊れているのではなく、LLM による SQL 生成が非決定的なためです。「Metric View は直接 JOIN できない」という一定の制約に対して、生成が正しい形と誤った形の間でぶれ、誤った形に振れたときにエラーになります。

ナレッジストアの JOIN 定義は「実行」ではなく「生成ヒント」

「じゃあ Genie のナレッジストアに JOIN 関係を定義すれば直る?」と考えたくなります。ここが理解のキモです。

ナレッジストアの JOIN 定義は、クエリを実行可能にするものではありません。 それは Genie(LLM)に「この 2 つは custkey で、多対一で関係する」と教えるメタデータ(生成のヒント) です。実行エンジンのレベルでは、Metric View は依然として直接 JOIN 不可のままです。

つまり Genie は、与えられた関係を実行可能な CTE 形へ「翻訳」 しなければなりません。揺れの正体は、この翻訳が成功するか(CTE 形になる)/失敗するか(直接 JOIN のまま出す)です。

実験:join 定義あり / なしで生成 SQL を比べる

同じ Metric View 2 つを載せた Genie エージェントを 2 つ用意し、片方だけナレッジストアに JOIN 定義を入れて、同じ質問を投げて生成 SQL を比較しました(Genie Conversation API で生成 SQL を取得)。

条件 試行数 生成パターン 直接 JOIN 失敗
JOIN 定義あり 4 問 すべて CTE 0 件(全成功)
JOIN 定義なし 3 問 すべて CTE 0 件(全成功)
  • この検証(検証用に新しく作った最小構成の Genie エージェントで、執筆時点の Genie を使用)では、合計 7/7 で CTE 形に翻訳され、直接 JOIN の失敗は再現しませんでした。
  • JOIN 定義の有無で差は出ず、定義なしでも Genie は自力で custkey を見つけて CTE 化しました。今回は両 Metric View に同名の custkey ディメンションがあり、関係が自明だったためと考えられます。

Genie は非決定的で、今回のサンプルは単純(共有キー 1 つ・メジャー少数)です。「再現しなかった」=「起きない」ではありません。実務のように結合キー名が異なる/関係が複雑/メジャーが多いケースでは、生成の揺れが観察されやすくなります。

この結果からの示唆:

  • JOIN 定義の本当の役割は「CTE 化させること」ではなく「どのキーで・どの関係で結ぶかを教えること」。 結合キー名が Metric View 間で異なるほど、この定義の価値(正しいキーで結ぶ)が効いてきます。
  • CTE 化させたいなら、Example SQL(正解の見本)で生成構造を固定するのが確実。

安定化のためのチューニング

効果の大きい順に挙げます。

1. instructions に明示する

Metric View を他の Metric View またはテーブルと JOIN する場合、
Metric View を直接 JOIN してはいけない。
必ず各 Metric View を個別の CTE として定義し、
必要な Measure は MEASURE() で集計し、JOIN キーの粒度で GROUP BY した後、
CTE 同士を JOIN すること。

2. Example SQL(CTE 形の見本)を登録する ← 最も効く

毎回の質問に対して人が SQL を書くのではありません。 代表的な横断質問を数本、CTE 形式の正解 SQL とセットで登録します。Genie はこれをお手本として学習し、似た質問に同じパターンを当てはめます。上の「正解:CTE で集計してから JOIN する」の SQL がそのまま見本になります。

3. JOIN キーと粒度を固定する

Example SQL と説明文で、結合キー・時間粒度・各 CTE の粒度・JOIN 後に再集計してよいメジャーか・多対一か一対多か・Fan-out が発生しないか、を明示します。

4. エージェントに載せる Metric View を絞る

1 つの Genie エージェントに登録できるテーブル/ビューの上限は 30 ですが、上限まで積まず、必要なものだけに絞って小さく始めるほど、選択のあいまいさが減り精度が安定します。

まとめ

  • Metric View は直接 JOIN 不可。METRIC_VIEW_JOIN_NOT_SUPPORTED を見たら「CTE 化してから JOIN」に直す。
  • Genie の揺れは非決定的生成が制約にぶつかった結果。ナレッジストアの JOIN 定義は生成ヒントであって実行保証ではない。
  • 安定化は Example SQL(CTE 形の見本)で生成を固定するのが本命。

後片付け

DROP SCHEMA IF EXISTS main.mv_demo CASCADE;

参考

はじめてのDatabricks

はじめてのDatabricks

Databricks無料トライアル

Databricks無料トライアル

2
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
2
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?