❄️

Snowflake Semantic Viewの新SQL構文がリリース

に公開

はじめに

Snowflakeが最近、Semantic Viewをクエリする際の新しいSQL構文をリリースしました。この新構文により、より直感的で標準的なSQLでSemantic Viewをクエリできるようになりました。

本記事では、実際にSnowflakeのTPC-Hサンプルデータを使って新構文を検証し、従来構文との違いや制約事項、ベストプラクティスを詳しく解説します。

Semantic Viewとは?

Semantic Viewは、Snowflakeが提供するセマンティックレイヤーの実装で、以下の特徴を持ちます:

  • 複数のテーブルを統合したビジネスロジックの定義
  • ディメンション(dimension)、ファクト(fact)、メトリクス(metric)の明確な区別
  • 再利用可能なビジネスメトリクスの定義
  • データモデルの抽象化によるクエリの簡素化

何が変わったのか?

従来の構文

従来はSEMANTIC_VIEW()句を使って、ディメンションとメトリクスを明示的に指定する必要がありました:

SELECT * FROM SEMANTIC_VIEW(
    customer_order_analysis
    DIMENSIONS customer.customer_market_segment
    METRICS orders.total_revenue, orders.average_order_value
)
ORDER BY customer_market_segment;

新しい構文

新構文では、Semantic Viewの名前を直接FROM句に指定し、標準的なGROUP BY構文を使用できます:

SELECT 
    customer_market_segment,
    AGG(total_revenue) AS revenue,
    AGG(average_order_value) AS avg_value
FROM customer_order_analysis
GROUP BY customer_market_segment
ORDER BY customer_market_segment;

ポイント:

  • より直感的で読みやすい
  • メトリクスはAGG()などの関数でラップする必要がある
  • サードパーティなどの外部ツールからセマンティックモデルにアクセスする場合にも特別な作り込みは必要ない

検証環境のセットアップ

使用データ

TPC-Hのサンプルデータ(Scale Factor 1)を使用します:

-- データベースとスキーマの作成
CREATE DATABASE IF NOT EXISTS semantic_view_test;
USE DATABASE semantic_view_test;
CREATE SCHEMA IF NOT EXISTS public;
USE SCHEMA public;

Semantic Viewの作成

顧客(CUSTOMER)、注文(ORDERS)、明細(LINEITEM)の3テーブルを統合したSemantic Viewを作成します:

CREATE OR REPLACE SEMANTIC VIEW customer_order_analysis
  TABLES (
    customer AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.CUSTOMER 
      PRIMARY KEY (C_CUSTKEY),
    orders AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS 
      PRIMARY KEY (O_ORDERKEY),
    lineitem AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.LINEITEM
  )
  RELATIONSHIPS (
    -- 注文 → 顧客へのリレーション
    orders_to_customer AS orders(O_CUSTKEY) 
      REFERENCES customer(C_CUSTKEY),
    -- 明細 → 注文へのリレーション
    lineitem_to_orders AS lineitem(L_ORDERKEY) 
      REFERENCES orders(O_ORDERKEY)
  )
  DIMENSIONS (
    -- 顧客ディメンション
    customer.customer_name AS C_NAME,
    customer.customer_market_segment AS C_MKTSEGMENT,
    customer.customer_nation AS C_NATIONKEY,
    
    -- 注文ディメンション
    orders.order_status AS O_ORDERSTATUS,
    orders.order_priority AS O_ORDERPRIORITY,
    orders.order_date AS O_ORDERDATE,
    orders.order_year AS YEAR(O_ORDERDATE),
    orders.order_month AS MONTH(O_ORDERDATE)
  )
  METRICS (
    -- 注文に関するメトリクス
    orders.total_order_count AS COUNT(*) 
      WITH SYNONYMS = ('注文件数', 'Total orders'),
    
    orders.total_revenue AS SUM(O_TOTALPRICE) 
      WITH SYNONYMS = ('総売上', 'Total revenue'),
    
    orders.average_order_value AS AVG(O_TOTALPRICE) 
      WITH SYNONYMS = ('平均注文額', 'Average order value'),
    
    orders.max_order_value AS MAX(O_TOTALPRICE) 
      WITH SYNONYMS = ('最大注文額', 'Maximum order value'),
    
    -- 明細に関するメトリクス
    lineitem.total_quantity AS SUM(L_QUANTITY) 
      WITH SYNONYMS = ('総数量', 'Total quantity'),
    
    lineitem.avg_discount AS AVG(L_DISCOUNT) 
      WITH SYNONYMS = ('平均割引率', 'Average discount')
  );

新構文の基本的な使い方

1. 基本的なGROUP BY

最もシンプルな例です。顧客セグメント別の集計を行います:

SELECT 
    customer_market_segment,
    AGG(total_order_count) AS order_count,
    AGG(total_revenue) AS revenue,
    AGG(average_order_value) AS avg_value
FROM customer_order_analysis
GROUP BY customer_market_segment
ORDER BY customer_market_segment;

結果例:

+---------------------------+-------------+-------------------+------------------+
| CUSTOMER_MARKET_SEGMENT   | ORDER_COUNT | REVENUE           | AVG_VALUE        |
+---------------------------+-------------+-------------------+------------------+
| AUTOMOBILE                | 29752       | 142570259.47      | 4792.55          |
| BUILDING                  | 30142       | 142425379.88      | 4725.92          |
| FURNITURE                 | 29968       | 142563633.14      | 4756.38          |
| HOUSEHOLD                 | 30189       | 141659947.53      | 4692.15          |
| MACHINERY                 | 29949       | 142655915.51      | 4764.00          |
+---------------------------+-------------+-------------------+------------------+

2. WHERE句を使ったフィルタリング

完了済み注文(status='F')のみを対象に集計します:

SELECT 
    customer_market_segment,
    AGG(total_order_count) AS order_count,
    AGG(total_revenue) AS revenue
FROM customer_order_analysis
WHERE order_status = 'F'  -- 完了済み注文のみ
GROUP BY customer_market_segment
ORDER BY customer_market_segment;

3. HAVING句を使った集計後のフィルタリング

総売上が3,000万ドルを超えるセグメントのみを抽出します:

SELECT 
    customer_market_segment,
    AGG(total_order_count) AS order_count,
    AGG(total_revenue) AS revenue
FROM customer_order_analysis
GROUP BY customer_market_segment
HAVING AGG(total_revenue) > 30000000
ORDER BY revenue DESC;

4. 複数ディメンションでのGROUP BY

顧客セグメントと注文ステータスの組み合わせで集計します:

SELECT 
    customer_market_segment,
    order_status,
    AGG(total_order_count) AS order_count,
    AGG(total_revenue) AS revenue
FROM customer_order_analysis
GROUP BY customer_market_segment, order_status
ORDER BY customer_market_segment, order_status
LIMIT 20;

複雑なクエリパターン

CTEとの組み合わせ

CTEを使って段階的な集計を行い、ランキングを付けます:

WITH segment_stats AS (
    SELECT 
        customer_market_segment,
        AGG(total_order_count) AS order_count,
        AGG(total_revenue) AS revenue,
        AGG(average_order_value) AS avg_value
    FROM customer_order_analysis
    GROUP BY customer_market_segment
)
SELECT 
    customer_market_segment,
    order_count,
    revenue,
    avg_value,
    RANK() OVER (ORDER BY revenue DESC) AS revenue_rank
FROM segment_stats
ORDER BY revenue_rank;

条件式(CASE)との組み合わせ

売上規模に応じてカテゴリ分けします:

SELECT 
    customer_market_segment,
    order_status,
    AGG(total_order_count) AS order_count,
    AGG(total_revenue) AS revenue,
    CASE 
        WHEN AGG(total_revenue) > 50000000 THEN 'High'
        WHEN AGG(total_revenue) > 30000000 THEN 'Medium'
        ELSE 'Low'
    END AS revenue_category
FROM customer_order_analysis
GROUP BY customer_market_segment, order_status
ORDER BY revenue DESC
LIMIT 20;

サブクエリでの割合計算

各セグメントの売上が全体に占める割合を計算します:

WITH segment_revenue AS (
    SELECT 
        customer_market_segment AS segment,
        AGG(total_revenue) AS revenue
    FROM customer_order_analysis
    GROUP BY customer_market_segment
),
total_calc AS (
    SELECT SUM(revenue) AS total_revenue
    FROM segment_revenue
)
SELECT 
    segment,
    revenue,
    ROUND(revenue / total_revenue * 100, 2) AS revenue_percentage
FROM segment_revenue
CROSS JOIN total_calc
ORDER BY revenue DESC;

結果例:

+-------------+-------------------+--------------------+
| SEGMENT     | REVENUE           | REVENUE_PERCENTAGE |
+-------------+-------------------+--------------------+
| MACHINERY   | 142655915.51      | 20.01              |
| AUTOMOBILE  | 142570259.47      | 20.00              |
| FURNITURE   | 142563633.14      | 19.99              |
| BUILDING    | 142425379.88      | 19.98              |
| HOUSEHOLD   | 141659947.53      | 19.87              |
+-------------+-------------------+--------------------+

時系列分析

年別・セグメント別の売上トレンドを分析します:

SELECT 
    order_year,
    customer_market_segment,
    AGG(total_order_count) AS order_count,
    AGG(total_revenue) AS revenue
FROM customer_order_analysis
GROUP BY order_year, customer_market_segment
ORDER BY order_year, customer_market_segment;

ウィンドウ関数メトリックを使った高度な分析

セマンティックビュー定義時にウィンドウ関数メトリックを組み込むことで、前年比、累積値、移動平均などの高度な分析をシンプルなクエリで実現できます。

ウィンドウ関数メトリックの定義

CREATE SEMANTIC VIEW customer_order_with_window_metrics
  TABLES (
    customer AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.CUSTOMER 
      PRIMARY KEY (C_CUSTKEY),
    orders AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS 
      PRIMARY KEY (O_ORDERKEY)
  )
  RELATIONSHIPS (
    orders_to_customer AS orders(O_CUSTKEY) 
      REFERENCES customer(C_CUSTKEY)
  )
  DIMENSIONS (
    customer.customer_market_segment AS C_MKTSEGMENT,
    orders.order_year AS YEAR(O_ORDERDATE)
  )
  METRICS (
    -- 基本メトリクス(先に定義する必要がある)
    orders.total_revenue AS SUM(O_TOTALPRICE),
    
    -- ウィンドウ関数メトリクス: 累積売上
    orders.cumulative_revenue AS SUM(total_revenue) OVER (
      PARTITION BY customer.customer_market_segment
      ORDER BY orders.order_year
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ),
    
    -- ウィンドウ関数メトリクス: 前年売上
    orders.prev_year_revenue AS LAG(total_revenue, 1) OVER (
      PARTITION BY customer.customer_market_segment
      ORDER BY orders.order_year
    ),
    
    -- ウィンドウ関数メトリクス: 前年比(%)
    orders.revenue_yoy_pct AS (
      (total_revenue - LAG(total_revenue, 1) OVER (
        PARTITION BY customer.customer_market_segment
        ORDER BY orders.order_year
      )) / LAG(total_revenue, 1) OVER (
        PARTITION BY customer.customer_market_segment
        ORDER BY orders.order_year
      ) * 100
    ),
    
    -- ウィンドウ関数メトリクス: 3年移動平均
    orders.moving_avg_3y AS AVG(total_revenue) OVER (
      PARTITION BY customer.customer_market_segment
      ORDER BY orders.order_year
      ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    )
  );

ウィンドウ関数メトリックを使ったクエリ

定義したウィンドウ関数メトリックを使うと、複雑な時系列分析もシンプルなクエリで実現できます:

-- 年別の売上推移と各種指標を一度に取得
SELECT 
    customer_market_segment,
    order_year,
    AGG(total_revenue) AS revenue,
    AGG(cumulative_revenue) AS cumulative_revenue,
    AGG(prev_year_revenue) AS prev_year_revenue,
    ROUND(AGG(revenue_yoy_pct), 2) AS yoy_pct,
    ROUND(AGG(moving_avg_3y), 2) AS moving_avg_3y
FROM customer_order_with_window_metrics
GROUP BY customer_market_segment, order_year
ORDER BY customer_market_segment, order_year;

結果例(AUTOMOBILEセグメント):

+--------+----------+-------------+---------------+--------+-------------+
| YEAR   | REVENUE  | CUMULATIVE  | PREV_YEAR_REV | YOY_%  | MOVING_AVG  |
+--------+----------+-------------+---------------+--------+-------------+
| 1992   | 9234567  | 9234567     | NULL          | NULL   | 9234567     |
| 1993   | 23456789 | 32691356    | 9234567       | 153.99 | 16345678    |
| 1994   | 28765432 | 61456788    | 23456789      | 22.64  | 20485596    |
| 1995   | 31234567 | 92691355    | 28765432      | 8.58   | 27818929    |
+--------+----------+-------------+---------------+--------+-------------+

特定のセグメントの詳細分析

-- AUTOMOBILEセグメントのみの時系列分析
SELECT 
    order_year,
    AGG(total_revenue) AS revenue,
    AGG(cumulative_revenue) AS cumulative_revenue,
    ROUND(AGG(revenue_yoy_pct), 2) AS yoy_pct,
    ROUND(AGG(moving_avg_3y), 2) AS moving_avg_3y
FROM customer_order_with_window_metrics
WHERE customer_market_segment = 'AUTOMOBILE'
GROUP BY order_year
ORDER BY order_year;

ウィンドウ関数メトリックの利点

  1. ビジネスロジックの一元管理:前年比などの計算式をSemantic Viewに集約
  2. クエリの簡素化:複雑なウィンドウ関数をクエリ側で書く必要がない
  3. 再利用性:定義したメトリクスを複数のクエリで共通利用できる
  4. 保守性向上:計算ロジックの変更がSemantic View定義のみで済む

制約事項とエラーケース

新構文には以下の制約があります。実際のエラーメッセージとともに解説します。

1. メトリクスにはAGG()が必須

エラーになる例:

SELECT 
    customer_market_segment,
    total_revenue  -- AGG()でラップしていない
FROM customer_order_analysis
GROUP BY customer_market_segment;

エラーメッセージ:

SQL compilation error: error line 3 at position 5
'CUSTOMER_ORDER_ANALYSIS.TOTAL_REVENUE' in select clause is 
neither an aggregate nor in the group by clause.

正しい例:

SELECT 
    customer_market_segment,
    AGG(total_revenue) AS revenue  -- AGG()でラップする
FROM customer_order_analysis
GROUP BY customer_market_segment;

2. メトリクスはGROUP BY句に含められない

エラーになる例:

SELECT 
    customer_market_segment,
    AGG(total_revenue) AS revenue
FROM customer_order_analysis
GROUP BY customer_market_segment, total_revenue;  -- メトリクスをGROUP BYに含めている

エラーメッセージ:

SQL compilation error:
Requested semantic expression 'ORDERS.TOTAL_REVENUE' in DIMENSIONS clause 
must be one of the following types: (DIMENSION, FACT).

3. クエリでのウィンドウ関数の直接使用は不可

クエリ内で直接ウィンドウ関数を使うことはできません。

エラーになる例:

SELECT 
    customer_market_segment AS segment,
    AGG(total_revenue) AS revenue,
    SUM(AGG(total_revenue)) OVER () AS total_revenue  -- ウィンドウ関数
FROM customer_order_analysis
GROUP BY customer_market_segment;

エラーメッセージ:

Unsupported feature 'WINDOW FUNCTIONS'.

推奨される解決策:ウィンドウ関数メトリックを使用

**最良の方法は、セマンティックビュー定義時にウィンドウ関数メトリックを組み込むことです。**これにより、クエリ側はシンプルなSELECT文で高度な分析が可能になります。

-- セマンティックビュー定義時にウィンドウ関数を組み込む
CREATE SEMANTIC VIEW customer_order_with_window_metrics
  TABLES (...)
  RELATIONSHIPS (...)
  DIMENSIONS (
    customer.customer_market_segment AS C_MKTSEGMENT
  )
  METRICS (
    -- 基本メトリクス(先に定義)
    orders.total_revenue AS SUM(O_TOTALPRICE),
    
    -- ウィンドウ関数メトリック: 全体に占める割合
    orders.revenue_percentage AS (
      total_revenue / SUM(total_revenue) OVER () * 100
    )
  );

クエリ側は非常にシンプルに:

SELECT 
    customer_market_segment,
    AGG(total_revenue) AS revenue,
    ROUND(AGG(revenue_percentage), 2) AS revenue_percentage
FROM customer_order_with_window_metrics
GROUP BY customer_market_segment
ORDER BY revenue DESC;

結果例:

+---------------------------+-------------------+--------------------+
| CUSTOMER_MARKET_SEGMENT   | REVENUE           | REVENUE_PERCENTAGE |
+---------------------------+-------------------+--------------------+
| MACHINERY                 | 142655915.51      | 20.01              |
| AUTOMOBILE                | 142570259.47      | 20.00              |
| FURNITURE                 | 142563633.14      | 19.99              |
+---------------------------+-------------------+--------------------+

代替案:CTEを使用

ウィンドウ関数メトリックを定義しない場合は、CTEで段階的に計算することもできます(詳細は「複雑なクエリパターン」セクションを参照)。

従来構文との結果比較

同じクエリを両方の構文で実行し、結果が一致することを確認します。

従来構文

SELECT * FROM SEMANTIC_VIEW(
    customer_order_analysis
    DIMENSIONS customer.customer_market_segment
    METRICS orders.total_revenue
)
ORDER BY customer_market_segment;

新構文

SELECT 
    customer_market_segment,
    AGG(total_revenue) AS total_revenue
FROM customer_order_analysis
GROUP BY customer_market_segment
ORDER BY customer_market_segment;

両方とも同じ結果が得られます:

+---------------------------+-------------------+
| CUSTOMER_MARKET_SEGMENT   | TOTAL_REVENUE     |
+---------------------------+-------------------+
| AUTOMOBILE                | 142570259.47      |
| BUILDING                  | 142425379.88      |
| FURNITURE                 | 142563633.14      |
| HOUSEHOLD                 | 141659947.53      |
| MACHINERY                 | 142655915.51      |
+---------------------------+-------------------+

パフォーマンス面での考察

実際に検証した結果、新構文と従来構文でパフォーマンス上の大きな差は見られませんでした。いずれも以下の特徴があります:

  • クエリプランは同等
  • スキャンするデータ量も同じ
  • 実行時間も同程度

パフォーマンスよりも、可読性標準SQLとの親和性が新構文の主なメリットです。

Semantic View作成時のハマりポイント

検証中に遭遇したエラーと解決策を共有します。

1. 句の順番が重要(FACTS、DIMENSIONS、METRICSの順)

Semantic Viewの定義では、句の順番が厳密に決まっています。特に、FACTSとMETRICSを両方定義する場合は、以下の順番を守る必要があります:

  1. TABLES
  2. RELATIONSHIPS
  3. FACTS(先に記述)
  4. DIMENSIONS
  5. METRICS(最後に記述)

エラーになる定義(順番が間違っている):

CREATE SEMANTIC VIEW customer_order_analysis
  TABLES (...)
  RELATIONSHIPS (...)
  DIMENSIONS (...)  -- 先にDIMENSIONSを書くと...
  FACTS (...)       -- FACTSがエラーになる!
  METRICS (...);

エラーメッセージ:

SQL compilation error:
syntax error line 14 at position 2 unexpected 'FACTS'.

正しい定義(正しい順番):

CREATE SEMANTIC VIEW customer_order_analysis
  TABLES (...)
  RELATIONSHIPS (...)
  FACTS (          -- 先にFACTSを定義
    orders.order_total_price AS O_TOTALPRICE
  )
  DIMENSIONS (     -- 次にDIMENSIONS
    customer.customer_market_segment AS C_MKTSEGMENT,
    orders.order_status AS O_ORDERSTATUS
  )
  METRICS (        -- 最後にMETRICS
    orders.total_order_count AS COUNT(*),
    orders.total_revenue AS SUM(O_TOTALPRICE),
    orders.average_order_value AS AVG(O_TOTALPRICE)
  );

参考: Snowflake公式ドキュメント - CREATE SEMANTIC VIEW

句の順番は重要です。例えば、 DIMENSIONS 句の前に FACTS 句を指定する必要があります。

FACTSとMETRICSの使い分け:

  • FACTS:行レベルの生データ(集約なし)を扱う場合に使用
  • METRICS:集約関数(SUM、AVG、COUNTなど)を使った計算指標を定義する場合に使用
  • 両方を定義することも可能:順番を守れば、FACTSとMETRICSを同時に定義できます

2. RELATIONSHIPSの方向に注意

エラーになる定義:

RELATIONSHIPS (
  customer(C_CUSTKEY) REFERENCES orders(O_CUSTKEY)  -- 逆!
)

エラーメッセージ:

SQL compilation error:
The referenced key in the relationship 'CUSTOMER REFERENCES ORDERS' 
must be the primary or unique key of the referenced entity.

正しい定義:

RELATIONSHIPS (
  orders(O_CUSTKEY) REFERENCES customer(C_CUSTKEY)  -- 子 → 親
)

リレーションシップは「外部キーを持つテーブル → プライマリキーを持つテーブル」の方向で定義します。

補足:FACTSとMETRICSを両方定義する完全な例

上記の2つの注意点を踏まえた、FACTSとMETRICSを両方定義する完全な例です:

CREATE SEMANTIC VIEW customer_order_with_facts_and_metrics
  TABLES (
    customer AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.CUSTOMER 
      PRIMARY KEY (C_CUSTKEY),
    orders AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS 
      PRIMARY KEY (O_ORDERKEY)
  )
  RELATIONSHIPS (
    -- 正しい方向:子テーブル → 親テーブル
    orders_to_customer AS orders(O_CUSTKEY) 
      REFERENCES customer(C_CUSTKEY)
  )
  FACTS (
    -- 先にFACTSを定義(順番重要!)
    orders.order_total_price AS O_TOTALPRICE,
    orders.order_clerk AS O_CLERK
  )
  DIMENSIONS (
    -- FACTSの後にDIMENSIONS
    customer.customer_market_segment AS C_MKTSEGMENT,
    orders.order_status AS O_ORDERSTATUS
  )
  METRICS (
    -- 最後にMETRICS
    orders.total_order_count AS COUNT(*),
    orders.total_revenue AS SUM(O_TOTALPRICE)
  );

このように定義すれば、同じビュー内でFACTS、DIMENSIONS、METRICSの全てを使用できます。

クエリ時の制約:ただし、クエリ時にはFACTSとMETRICSを同じSEMANTIC_VIEW句内で同時に指定することはできません。どちらか一方を選択する必要があります。

-- ✅ FACTSのみ使用
SELECT * FROM SEMANTIC_VIEW(
    customer_order_with_facts_and_metrics
    DIMENSIONS customer.customer_market_segment
    FACTS orders.order_total_price
);

-- ✅ METRICSのみ使用
SELECT * FROM SEMANTIC_VIEW(
    customer_order_with_facts_and_metrics
    DIMENSIONS customer.customer_market_segment
    METRICS orders.total_revenue
);

-- ❌ 両方は同時に指定できない
-- SELECT * FROM SEMANTIC_VIEW(
--     customer_order_with_facts_and_metrics
--     DIMENSIONS customer.customer_market_segment
--     FACTS orders.order_total_price
--     METRICS orders.total_revenue  -- エラー
-- );
-- エラー内容: 
-- SQL compilation error:
-- Invalid semantic view query: Facts and metrics cannot be requested in the -- same query.

ベストプラクティス

検証を通じて見えてきたベストプラクティスをまとめます:

1. 新構文を優先的に使う

標準SQLに近い新構文を使うことで:

  • コードの可読性が向上
  • SQLに慣れた開発者なら直感的に理解できる
  • BIツールとの統合がしやすい

2. ウィンドウ関数メトリックを活用する

複雑な分析(前年比、累積値、移動平均など)は、ウィンドウ関数メトリックとしてSemantic View定義に組み込むことを推奨します:

メリット:

  • ビジネスロジックが一元管理される
  • クエリがシンプルになる
  • 再利用性が高まる
  • 計算式の変更が容易になる

例:

METRICS (
  -- 基本メトリクス
  orders.total_revenue AS SUM(O_TOTALPRICE),
  
  -- ウィンドウ関数メトリック
  orders.revenue_yoy_pct AS (
    (total_revenue - LAG(total_revenue, 1) OVER (...)) 
    / LAG(total_revenue, 1) OVER (...) * 100
  )
)

これにより、クエリ側はAGG(revenue_yoy_pct)を呼び出すだけで前年比が取得できます。

3. CTEで段階的な処理を行う

ウィンドウ関数メトリックを定義しない場合や、複雑な多段階の集計が必要な場合は、CTEで段階的に行うことで:

  • ロジックが明確になる
  • デバッグしやすくなる
  • 可読性が向上する

4. メトリクスは意味のある名前を付ける

METRICS (
  orders.total_revenue AS SUM(O_TOTALPRICE) 
    WITH SYNONYMS = ('総売上', 'Total revenue')  -- シノニムも設定
)

シノニムを設定することで:

  • 自然言語でのクエリをサポート(Snowflake Copilotなど)
  • 多言語対応が可能

5. ディメンションの粒度を意識する

ディメンションとメトリクスの関係性(粒度)を考慮してクエリを設計します。粒度が合わない場合は、どのディメンションが使えるかを確認できます:

SHOW SEMANTIC DIMENSIONS IN customer_order_analysis 
FOR METRIC total_revenue;

6. 句の順番に注意する(重要!)

Semantic Viewを定義する際は、句の順番が厳密に決まっています

CREATE SEMANTIC VIEW view_name
  TABLES (...)
  RELATIONSHIPS (...)
  FACTS (...)        -- 先に記述
  DIMENSIONS (...)   -- 次に記述
  METRICS (...);     -- 最後に記述

この順番を守らないとエラーになります。特に、FACTSとMETRICSを両方定義する場合は要注意です。

まとめ

Snowflake Semantic Viewの新SQL構文を検証した結果、以下のことがわかりました:

メリット

✅ 標準SQLとの親和性が高く、直感的に使える
✅ 従来構文と同等のパフォーマンス
✅ CTEやサブクエリとの組み合わせが容易
ウィンドウ関数メトリックにより、高度な分析をシンプルなクエリで実現可能

注意点

⚠️ メトリクスには必ずAGG()などの関数が必要
⚠️ クエリ内でウィンドウ関数は直接使えない(ウィンドウ関数メトリックとして定義することを推奨)
⚠️ メトリクスはGROUP BY句に含められない

どちらを使うべきか?

新規開発では新SQL構文を利用するのをお薦めします。
その理由は:

  1. 標準SQLに近く、学習コストが低い
  2. 可読性が高く、メンテナンスしやすい
  3. 従来構文と機能的な差はない

ただし、既存のクエリを動かす場合や、特定の制約がある場合は従来構文も選択肢になります。

参考資料

おわりに

Semantic Viewの新構文は、セマンティックレイヤーをより身近なものにしてくれる素晴らしいアップデートです。標準SQLに慣れた開発者なら、学習コストをほとんどかけずに使い始められます。

Snowflake Japan

Discussion