
2026/04/09 23:48
Metrics SQL:人間とエージェントのために設計された、SQL ベースのセマンティックレイヤー。
RSS: https://news.ycombinator.com/rss
要約▶
日本語翻訳:
Rill は、ビジネス指標をコアデータプリミティブとして扱うことで、SQL をビジネス指標のための汎用言語確立を目指す。このアプローチは、dbt、Looker、Metabase、Python ノートブック、Slack ボット、AI エージェントといったツール間でロジック定義が分岐する「指標ドリフト」という問題を直接的に対抗するため、アプリケーションによらず一貫した指標セマンティクスを確保します。Apache Calcite の創始者である Julian Hyde が提唱する概念に基づき、データベースエンジン自体が指標のセマンティクスをネイティブに理解できるようにし、それを知的 OLAP クーブへ実質的に変換します。ユーザーは YAML 構造(埋め込まれた SQL を含む)を用いてディメンションコンテキスト内にて尺度(集計表現)とディメンションを定義し、AI 指示や時系列カラム、最小時間粒度などのメタデータで強化されます。Metrics SQL はこれらのビューをテーブルのように手動集計なしでクエリ可能とし、トップリスト、フィルター、時間範囲をサポートしますが、ビュー間の JOIN や SELECT * を禁じるなど制限もあります。アーキテクチャはクエリを Parser(バリデーション)、Query Compiler(名前解決およびセマンティック推論)、Executor(セキュリティおよび最適化)の 3 レイヤで処理します。今後の開発は「セマンティックプッシュダウン」に注力し、MEASURE セマンティクスをエンジンレベルへ移転することでゼロホップクエリとネイティブ最適化を実現します。この統一された SQL ベースのアプローチにより運用が簡素化され、エンタープライズ環境における既存 BI ツールおよび高度な AI エージェントとのシームレスな統合の道を開きます。
本文
SQL は、データの共通言語として 40 年以上にわたって広く使われてきました。私たちは、Rill を構築する際にもこの決断を踏まえ、メトリクスベースのセマンティックレイヤーにおける共通言語として SQL を採用することにしました。Rill を開発する中で、私たちは「収益」「MAU(月間アクティブユーザー数)」「ROAS(広告費対売上比率)」といった概念であるメトリクスこそが、セマンティックレイヤーの中核となる基本要素であると確信していました。また、セマンティックレイヤー(およびそのメトリクス)へのクエリ実行に、新しい独自言語や API を学ぶ必要はないと考えるとともに、セマンティックレイヤーは既にあらゆるデータベース、BI ツール、そして AI エージェントが熟知している SQL という言語を話すべきだと考えていました。
本ブログ投稿では、Rill の Metrics SQL がどのように機能するか、なぜそれをそのような方法で構築したか、そして今後どこへ向かわせているかを技術的な深層分析の視点から詳しく解説します。
SQL とメトリクスを使用した、決定的で安全かつ高速なセマンティックレイヤーの実装
現代的な分析スタックにおいて、「アクティブユーザー一人あたりの収益」といったビジネス上のメトリクスは、複数の場所で定義される必要があります:dbt モデル、Looker の Explore、Metabase のクエリ、Python ノートブック、Slack ボット、そして AI エージェントのメタデータなどです。時間が経過するにつれて、それぞれの定義は互いに乖離してしまいます。財務部門から「なぜ私の数字とあなたの数字が一致しないのか?」という質問に対して、一般的な答えは「計算方法が異なるからです」というものになるのが常です。
「一度ビジネスロジックを定義し、あらゆる場所でクエリを実行する」というメトリクスレイヤーの概念自体は何年も前から存在しています。しかし、実装においては常に同様の罠に陥ってきました:新たなクエリ言語を発明することです。GraphQL クエリ、専用 MDX 表現式、または JSON-over-REST API はいずれも、新しい抽象概念を学ぶ必要があります。これらは既存の SQL ツールとの互換性を損ない、ビジネスロジックとアプリケーションの間の障壁となります。
Apache Calcite の共同創始者であり、SQL エコシステムのパイオニアである Julian Hyde は、その Data Council での講演において、メトリクスにはファーストクラスの SQL 表現が必要であると提唱しました。これは SQL が完璧だからという理由ではなく、むしろ因为它が普遍的であるから——すなわち、あらゆるツール、エンジニア、そしてあらゆる AI モデルがすでに話し合っている言語だからです。Julian は SQL に FAIR クラスの MEASURE セマンティクスを導入するよう提案しました。具体的には、集計関数をインライン表現としてではなくセマンティックオブジェクトとして宣言し、データベースエンジンが文脈を正しく理解してメトリクスを計算できるようにすることであります。
この視点は私たちに強く共鳴し、私たちは SQL によってクエリ可能なファーストプライオリティのメトリクス型セマンティックレイヤーを構築しました。これにより次のことが可能になりました:
- 決定的な单一の真実源: ダッシュボード、ノートブック、API、AI エージェントなど、すべての消費者が同じ管轄下にある定義からメトリクスを取得します。複製も乖離もないため、収益メトリクスをクエリするエージェントは常に同じ結果を返し、生テーブルスキーマからの推測表現に基づくものではありません。
- アクセスポリシーを持つユニバーサルインターフェース: SQL は人間アナリストと LLM(大規模言語モデル)の両方に適用可能です。新しい言語を学ぶ必要も、モデルのファインチューニングを行う必要もありません。行レベルのセキュリティやアクセスルールはこのインターフェース上で一度定義され、誰または何が問い合わせているかにかわらず、すべてのクエリに対して強制されます。
- 高性能なアーキテクチャ: メトリクスで SQL を拡張することで、SQL ベースの最適化への道が開かれます。マテリアライズドビューが生のファクトテーブルではなくメトリクスクエリを処理することで、パフォーマンスとコストの改善が可能です。データベースインデックスやプロジェクションは、クエリパターンに基づいて知的に調整されます。
Rill のメトリクスレイヤーは Measure と Dimension によって構成される
メトリクスとは何でしょうか?Rill において、メトリクスとは「次元コンテキスト内で評価される集計 Measure 表現式」——具体的には総収益、平均セッション時間、または広告費対売上比率(ROAS)など——として定義されます。図に示す通り、収益メトリクスは特定の地域、製品カテゴリ、および時間次元に対してフィルタリングすることができます。これはビジネスインテリジェンスシステムにおけるよく知られた概念である OLAP キューブに対応しています。
Dimension は切り分けたりグループ化したりする属性のことです:地理、製品カテゴリ、チャネル、日付などです。SQL 用語では、これらは GROUP BY に登場します。Dimension は生カラム(例:
country)也可以是計算された表現式(例:dictGet('category_dict', 'product_category', product_id))でもあり得ます。
Measure と Dimension の他にも、Rill メトリクスレイヤーでは追加のメタデータプロパティを定義することができます:
- AI Instructions: メタデータ以外のビジネス文脈であり、AI エージェントにとって有用な情報です。例:会計年度の開始日と終了日の定義など。
- 時系列カラムおよび任意の最小時間粒度。
Metrics SQL とその OLAP SQL への変換
Metrics SQL は、メトリクスビューをテーブルのようにクエリできるようにする SQL の方言です。クエリ内での集計ロジックを記述せずに、次元と Measure を名前によって選択し、フィルタリング、ソート、結果の制限を行うことができます。Metrics SQL は、トップリスト、次元フィルタ、時間範囲クエリ、および比較といったビジネスロジクスを、はるかに少ない複雑さで、トークン数も少なく表現することを可能にします。
Metrics SQL クエリは、OLAP エンジンに到達する前に 3 つの論理的レイヤーを経由します:
- パーサー: クエリを解析し、その構文を検証します。
- クエリコンパイラー: 各名前をメトリクスビュー定義に対して解決し、Measure と Dimension を分類して、推測された GROUP BY を追加します。
- 実行器: セキュリティフィルタやセマンティックリライティングを適用した後、完全に形成されたパラメータ化された SQL 文字列を OLAP エンジンに渡します。
メトリクスビューの定義
Rill のメトリクスビュー定義は、SQL が埋め込まれた YAML を使用しています。YAML が構造的外郭を提供し、SQL がその中に埋め込まれた表現レベルのロジックを担当します。以下は
metrics_view 定義の例です。
# revenue_metrics.yaml type: metrics_view model: revenue_model timeseries: order_date smallest_time_grain: hour measures: - name: revenue expression: sum(order_usd) display_name: Total Revenue format_preset: currency_usd - name: order_volume expression: count(distinct order_id) display_name: Order Volume format_preset: humanize dimensions: - name: country column: country display_name: Country - name: product_category expression: dictGet('category_dict', 'product_category', product_id) display_name: Product Category
具体例:Metrics SQL からデータベース用 SQL への変換
以下は、OLAP エンジンからの基盤となる SQL に Metrics SQL がどのようにマッピングされるかを示す実在の変換例です。これらの例では ClickHouse をターゲットデータベースエンジンとして使用していますが、值得注意的是、Metrics SQL は DuckDB、Snowflake、Druid SQL 方言など他のものを支えております。
例 1: 収益でトップ 10 の国を表示する
-- Metrics SQL: SELECT country, revenue FROM revenue_metrics ORDER BY revenue DESC LIMIT 10 -- Output SQL: SELECT ("country") AS "country", sum(order_usd) AS "revenue" FROM "revenue_model" GROUP BY 1 ORDER BY "revenue" DESC NULLS LAST LIMIT 10
出力で注目すべき点:
- FROM クロウは基盤となるテーブルに書き換えられます。
- revenue メトリクスは集計表現
に展開されます。sum(order_usd) - GROUP BY は選択された次元のセットから推測されます。
例 2: 計算された Dimension に対するフィルタリング
-- Metrics SQL: SELECT country, revenue FROM revenue_metrics WHERE product_category = 'Electronics' ORDER BY revenue DESC LIMIT 10 -- ClickHouse SQL: SELECT ("country") AS "country", sum(order_usd) AS "revenue" FROM "revenue_model" WHERE (dictGet('category_dict', 'product_category', product_id) = ?) GROUP BY 1 ORDER BY "revenue" DESC NULLS LAST LIMIT 10 -- args: ["Electronics"]
この例では
product_category は計算された dimension です。コンパイラはフィルタ内でその dictGet 表現式を透明に展開します。文字列 'Electronics' はパラメータ化された引数に変換され、クエリの発生源に関係なく SQL インジェクションの安全性が確保されます。
例 3: フィルタ付きサブクエリ
Metrics SQL ではフィルタ付きのサブクエリをサポートしています。外部クエリは、内部クエリで生成された値のセットを使用して Dimension をフィルタリングできます。内部クエリ自体では HAVING を使用して Measure に対してフィルタリングすることも可能です。これにより、「総収益が閾値を超過した国における注文ボリュームを表示」といったパターンを表現できます。
-- Metrics SQL: SELECT country, order_volume FROM revenue_metrics WHERE country IN (SELECT country FROM revenue_metrics HAVING revenue > 10000) -- ClickHouse SQL: SELECT ("country") AS "country", (count(distinct order_id)) AS "order_volume" FROM "revenue_model" WHERE ("country") IN ( SELECT "country" FROM ( SELECT ("t1"."country") AS "country", ("t1"."revenue") AS "revenue" FROM ( SELECT ("country") AS "country", (sum(order_usd)) AS "revenue" FROM "revenue_model" GROUP BY 1 ) t1 WHERE (("t1"."revenue") > ?) ) ) GROUP BY 1 -- args: [10000]
内部の
HAVING revenue > 10000 により、集計が内部サブクエリに移動し、フィルタは外部レベルの WHERE に変換されます。外部クエリはその結果セットを IN リストとして使用して、メインクエリの Dimension をフィルタリングします。
例 4: ダイナミックな時間範囲表現式
-- Metrics SQL: SELECT country, revenue FROM revenue_metrics WHERE order_date > time_range_start('7D as of watermark') AND order_date <= time_range_end('7D as of watermark') -- ClickHouse SQL: SELECT ("country") AS "country", (sum(order_usd)) AS "revenue" FROM "revenue_model" WHERE (("order_date") > ? AND ("order_date") <= ?) GROUP BY 1 -- args: [2025-03-29T00:00:00Z, 2025-04-05T00:00:00Z]
time_range_start および time_range_end 関数は、Rill の時間構文表現式を受け付け、ウォーターマーク相対オフセットや粒度整列演算子をサポートしています。コンパイラはパース時にメトリクスビューのデータウォーターマーク对这些表現式を解決し、生成されたタイムスタンプはパラメータ化された引数となります。
例 5: ウィンドウ関数 Measure
ウィンドウ関数 Measure は、フレームのアタッチに時間 Dimension を必要とします。Measure
revenue_7day_avg は、次のように metrics view で定義されています:
measures: - name: revenue_7day_avg display_name: 7-Day Rolling Average Revenue expression: AVG(revenue) requires: [revenue] window: order: order_date frame: RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW
-- Metrics SQL: SELECT order_date, revenue_7day_avg FROM revenue_metrics -- ClickHouse SQL: SELECT ("t1"."order_date") AS "order_date", (AVG(revenue) OVER ( ORDER BY "t1"."order_date" RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW )) AS "revenue_7day_avg" FROM ( SELECT ("order_date") AS "order_date", (sum(order_usd)) AS "revenue" FROM "revenue_model" GROUP BY 1 ) t1
メトリクスレイヤーは 2 レベルのクエリを生成します:内部サブクエリでは日付によってグループ化された収益をベース集計として計算し、外部クエリではそれらの結果に対してウィンドウ関数を適用します。
requires フィールドは、ユーザーが明示的に選択していなくても、実行器が内部クエリに含めるべきベース Measure を指定します。
CLI、HTTP、および Claude を使用して Metrics SQL クエリを実行する
Rill は、Metrics SQL を使用してメトリクスをクエリするための複数の方法を提供しています。
1. CLI
Rill はコマンドラインから直接 Metrics SQL を提供しています。ローカルにプロジェクトを実行している場合:
# ローカル Rill プロジェクトを開始する rill start # メトリクスビューを Metrics SQL でクエリする rill query --local \ --resolver metrics_sql \ --properties sql="<Metrics SQL query>"
Rill Cloud プロジェクトへのターゲットも同様に機能し、プロジェクト名を代用します:
rill query --project my-project \ --resolver metrics_sql \ --properties sql="<Metrics SQL query>"
2. HTTP API
Rill は組み込みの HTTP エンドポイントを提供しています:
curl -X POST "https://admin.rilldata.com//v1/orgs/{org}/projects/{project}/runtime/api/metrics-sql" \ -H "Authorization: Bearer $RILL_TOKEN" \ -H "Content-Type: application/json" \ -d '{"sql": "<A valid metrics SQL>"}'
AI エージェントからメトリクスをクエリする:Claude/ChatGPT
Rill は、メトリクスレイヤーを AI エージェントに直接暴露する Model Context Protocol (MCP) サーバーを提供しています。Claude を生のデータウェアハウスに接続させるのではなく、Rill に接続させます。すると、AI が提供するすべての回答は、ダッシュボードと同じ管轄下の Measure とセキュリティポリシーに基づいて裏付けられます。
Rill のメトリクスレイヤーと AI を使用するには 2 つの方法があります:
- Rill の組み込み AI チャット: これはダッシュボードの一部として提供され、追加の設定を必要としません。プロジェクトを開き、AI タブをクリックして、通常の英語で質問を入力してください。Rill は Metrics SQL の生成とクエリエグゼクションを担当し、ブラウザ上で直接管轄された正確な回答が得られます。
- MCP を使用して Claude との接続: Claude は MCP 経由であらゆる Rill プロジェクトに接続できます。接続および設定に関する詳細はドキュメントにてご参照ください。一旦接続されると、Claude はメトリクスビューを閲覧し、Dimension と Measure を発見し、「製品カテゴリ別での収益の週間対週間の変化は何ですか?」といった質問に対して、定義されたビジネスロジックに基づいた結果で回答できます。
また、メトリクスビュー YAML に
ai_instructions を追加することで、Measure の解釈方法に関する追加の文脈を Rill に与え、ドメイン固有の質問に対する回答品質を向上させることができます。
現在の制限事項
Metrics SQL は意図的に SQL の一部 Subset として設計されています。これらは欠陥ではなく設計上の制約であり、コンパイルモデルの扱いやすさとセキュリティ保証の監査可能性を確保しています:
- メトリクスビュー間の JOIN は不可。各 Metrics SQL クエリは正確に 1 つのメトリクスビューをターゲットとします。クロスビュー分析には、セマンティックレイヤーで結合またはノーマライズされたモデルを定義する必要があります。
- **SELECT *は使用できません。Dimension と Measure は明示的に名前を指定する必要があります。これはガバナンス機構でありパフォーマンス最適化でもあります:OLAP クエリは決して不要なカラムをスキャンすべきではありません。
- Measure フィルタには HAVING を使用し、WHERE は使用できません。WHERE フィルタは事前集計の下位テーブルに対して適用されます——そこで有効なのは Dimension フィルタのみです。Measure に対するフィルタリング(例:total_spend > 10)には HAVING を使用する必要があります。
- Metrics SQL は絶えず進化しており、まだすべての演算子/表現式をサポートしていません。
未来:セマンティックプッシュダウン、メトリクスをデータベースレイヤーへ
現在のアーキテクチャでは、Metrics SQL は Rill のランタイムレイヤーでコンパイルされ、ネイティブな OLAP SQL が生成されます。これは強力ですが、すべてのクエリが Rill のランタイムを経由する必要があります。長期的なビジョンは、OLAP エンジン自体が Metrics セマンティクスをネイティブに理解することです。もし DuckDB や ClickHouse、Snowflake などのデータベースが MEASURE をファーストクラスの SQL キーワードとして理解できるならば、メトリクスビューは既存のテーブルメタデータと共に格納されるネイティブなデータベースオブジェクトとなり得ます。これにより、すべてのコンパイルとクエリオプティマイゼーションがエンジン内部に移動します。
これにより次のことが可能になります:
- ホップゼロのメトリクスクエリ: psql などのツールが中間マiddleware を介さずにデータベースから直接メトリクスをクエリできるようになります。
- データベースレベルの最適化: データベースは Measure 展開と外部クエリ全体を単一のプランとして最適化できます。エンジンはテーブル統計をより効果的に活用して、より具体的なクエリオプティマイゼーションを実行できます。
Rill の Metrics SQL レイヤーは、今日のこのビジョンの実装です——エンジンがネイティブなより豊かな Metrics セマンティクスを採用する際に、インターフェースは変わらず、変化するだけなのはコンパイルターゲットだけです。
スタート
Metrics SQL は Rill Developer(ローカル)および Rill Cloud で利用可能です。メトリクスビューを定義した直後、CLI、HTTP API、または接続された BI ツールから即時にクエリを実行できます。
# Rill をインストールする curl -s https://cdn.rilldata.com/install.sh | bash # プロジェクトを開始する rill start
完全な Metrics SQL のリファレンス——サポートされているフィルタ関数、時間範囲表現式、およびセキュリティモデルを含め——は Rill のドキュメントをご覧ください。