日本語カラム名のテーブルに dbt Semantic Layer のセマンティックモデルを定義する
はじめに
カラム名が日本語のテーブルに対して、dbt Semantic Layer(MetricFlow)のセマンティックモデルを定義できるかを検証した際の内容を記事としました。
dbt Semantic Layer における日本語カラム名対応
先に結論を述べると、dbt Semantic Layer では YAML でセマンティックモデルを定義する際、エンティティ名・ディメンション名・メトリクス名といったセマンティック識別子に ASCII 文字しか使用できません。
そのため、DWH 上のテーブルに日本語カラム名が含まれている場合entity: name:やdimension: name:を明示的に指定しないと、カラム名がそのままセマンティック識別子として使われてしまい、パースが通りません。
entity: name:やdimension: name:に ASCII 名を指定することでセマンティック識別子を上書きでき、この問題を回避できます。
以降で、各設定パターンを確認してみます。
前提条件
検証環境
以下の環境を使用しています。
- DWH:Snowflake
- dbt
- dbt platform(dbt-fusion)
また、この記事では dbt Fusion エンジンおよび dbt platform Latest リリーストラックで利用可能な新しい YAML 記法(モデルのcolumns配下にセマンティックモデル定義を記述する形式)を使用しています。
事前準備
検証用にstg_ordersとして日本語カラム名を含むモデルstg_test_japaneseを作成します。金額は検証用のダミー数値カラムです。
{{
config(
materialized='table'
)
}}
select
order_id as "注文ID",
customer_id as "顧客ID",
order_date as "注文日",
status as "ステータス",
abs(hash(order_id)) % 9901 + 100 as "金額"
from {{ ref('stg_orders') }}
日本語カラム名テーブルへのセマンティックモデル定義
パターン1:name:に日本語をそのまま記述した場合
はじめに、columns: - name:に日本語をそのまま記述してみます。
version: 2
models:
- name: stg_test_japanese
description: 日本語カラム名の検証用モデル
semantic_model:
enabled: true
agg_time_dimension: 注文日
columns:
- name: 注文ID
entity:
type: primary
name: japanese_orders
- name: 注文日
granularity: day
dimension:
type: time
- name: ステータス
dimension:
type: categorical
metrics:
- name: 注文数
type: simple
agg: count
expr: 1
dbt parseを実行すると以下のエラーが発生します。
$ dbt parse
14:25:41 error: dbt0102: The metric name '注文数' is invalid. It must begin with a letter
14:25:42 error: dbt1005: Invalid name `注文日` - names may only contain lower case letters, numbers, and underscores. ...
14:25:42 error: dbt1005: Invalid name `ステータス` - names may only contain lower case letters, numbers, and underscores. ...
14:25:42 error: dbt1005: Invalid Semantic Manifest
==================== Execution Summary =====================
Finished 'parse' with 4 errors for target 'dev' [198ms]
columns: - name:の値はそのままセマンティック識別子として検証されるため、日本語は使用できません。
パターン2:ダブルクォートで囲む
次に、YAML のダブルクォートで値を囲んでみます。
columns:
- name: "注文ID"
entity:
type: primary
name: japanese_orders
- name: "注文日"
granularity: day
dimension:
type: time
- name: "ステータス"
dimension:
type: categorical
$ dbt parse
14:27:07 error: dbt1005: Invalid name `注文日` - names may only contain lower case letters, numbers, and underscores. ...
14:27:07 error: dbt1005: Invalid name `ステータス` - names may only contain lower case letters, numbers, and underscores. ...
14:27:07 error: dbt1005: Invalid Semantic Manifest
==================== Execution Summary =====================
Finished 'parse' with 3 errors for target 'dev' [116ms]
YAML のダブルクォートは文字列リテラルの記法であり、値は注文日と同じ(ダブルクォートは消える)です。MetricFlow の識別子制約を回避できません。
パターン3:'"日本語"'形式でname:上書きなし
シングルクォート内にダブルクォートを含める形式('"注文日"')で物理カラム名を参照できます。ただし、この場合はダブルクォートを含む文字列("注文日")がそのままセマンティック識別子として使われます。
columns:
- name: '"注文ID"'
entity:
type: primary
name: japanese_orders
- name: '"注文日"'
granularity: day
dimension:
type: time # name: の上書きなし
- name: '"ステータス"'
dimension:
type: categorical # name: の上書きなし
$ dbt parse
error: dbt1005: Invalid name `"注文日"` - names may only contain lower case letters, numbers, and underscores. ...
error: dbt1005: Invalid name `"ステータス"` - names may only contain lower case letters, numbers, and underscores. ...
error: dbt1005: Invalid Semantic Manifest
Finished 'parse' with 3 errors for target 'dev'
ダブルクォート文字(")も識別子として無効のため、エラーとなります。
'"日本語"'形式 + dimension: name: / entity: name:で ASCII 上書き
columns: - name:に先と同じ手順で物理カラム名を指定し、dimension: name: / entity: name:を追加設定します。
これにより、MetricFlow のセマンティック名が上書きされます。セマンティック識別子は ASCII 名となるため、パースに成功します。
version: 2
models:
- name: stg_test_japanese
description: 日本語カラム名の検証用モデル
semantic_model:
enabled: true
agg_time_dimension: order_date_ja # time dimension の name: と一致させる
columns:
- name: '"注文ID"'
description: 注文の一意識別子
entity:
type: primary
name: japanese_orders
- name: '"注文日"'
description: 注文が作成された日付
granularity: day
dimension:
type: time
name: order_date_ja # agg_time_dimension から参照される ASCII 名
- name: '"ステータス"'
description: 注文のステータス
dimension:
type: categorical
name: status # MetricFlow のセマンティック名(ASCII 必須)
- name: '"顧客ID"'
description: 顧客の識別子
metrics:
- name: order_count_ja
type: simple
agg: count
expr: 1
$ dbt parse
14:34:20 Artifact written to target/semantic_manifest.json
14:34:20 Artifact written to target/manifest.json
==================== Execution Summary =====================
Finished 'parse' successfully for target 'dev' [120ms]
パースに成功しました。ディメンションの一覧とクエリも問題なく動作します。
$ dbt sl list dimensions --metrics order_count_ja
The list of available dimensions:
- japanese_orders__order_date_ja
- japanese_orders__status
- metric_time
$ dbt sl query --metrics order_count_ja --group-by japanese_orders__status
+-------------------------+----------------+
| JAPANESE_ORDERS__STATUS | ORDER_COUNT_JA |
+-------------------------+----------------+
| completed | 67 |
| shipped | 13 |
| return_pending | 2 |
| placed | 13 |
| returned | 4 |
+-------------------------+----------------+
--compileで生成される SQL を確認すると、日本語カラムが引用符付きで正しく参照されていることが分かります。
SELECT
japanese_orders__status
, SUM(__order_count_ja) AS order_count_ja
FROM (
SELECT
"ステータス" AS japanese_orders__status
, 1 AS __order_count_ja
FROM dbt_sl_db.dbt_tyasuhara.stg_test_japanese stg_test_japanese_src_10000
) subq_3
GROUP BY
japanese_orders__status
LIMIT 100
metrics の expr で日本語カラムを計算する
metrics: expr:は SQL 式として評価されるため、"金額" のような日本語カラム名と計算式をそのまま記述できます。
metrics:
- name: tax_amount_ja
type: simple
agg: sum
expr: '"金額" * 0.1' # 日本語カラムへの参照 + 計算式
- name: total_amount_ja
type: simple
agg: sum
expr: '"金額"' # 日本語カラムの直接参照
$ dbt sl query --metrics tax_amount_ja,total_amount_ja --group-by japanese_orders__status
+-------------------------+---------------+-----------------+
| JAPANESE_ORDERS__STATUS | TAX_AMOUNT_JA | TOTAL_AMOUNT_JA |
+-------------------------+---------------+-----------------+
| returned | 1354.7 | 13547 |
| completed | 35087.9 | 350879 |
| return_pending | 546.6 | 5466 |
| shipped | 5788.4 | 57884 |
| placed | 5821.4 | 58214 |
+-------------------------+---------------+-----------------+
コンパイル SQL:
$ dbt sl query --metrics tax_amount_ja,total_amount_ja --group-by japanese_orders__status --compile
SELECT
japanese_orders__status
, SUM(__tax_amount_ja) AS tax_amount_ja
, SUM(__total_amount_ja) AS total_amount_ja
FROM (
SELECT
"ステータス" AS japanese_orders__status
, "金額" * 0.1 AS __tax_amount_ja
, "金額" AS __total_amount_ja
FROM dbt_sl_db.dbt_tyasuhara.stg_test_japanese stg_test_japanese_src_10000
) subq_3
GROUP BY
japanese_orders__status
LIMIT 100
derived_semantics の expr で複合主キーを定義する
derived_semantics: entities: expr:も SQL 式として評価されるため、"注文ID" のような日本語カラム名をそのまま使えます。
derived_semantics:
entities:
- name: japanese_orders # セマンティック名は ASCII
type: primary
expr: "CAST(\"注文ID\" AS VARCHAR) || '-' || CAST(\"顧客ID\" AS VARCHAR)"
$ dbt sl query --metrics order_count_ja --group-by japanese_orders
+-----------------+----------------+
| JAPANESE_ORDERS | ORDER_COUNT_JA |
+-----------------+----------------+
| 2-3 | 1 |
| 17-71 | 1 |
・・・
+-----------------+----------------+
コンパイル SQL:
dbt sl query --metrics order_count_ja --group-by japanese_orders --compile
SELECT
japanese_orders
, SUM(__order_count_ja) AS order_count_ja
FROM (
SELECT
CAST("注文ID" AS VARCHAR) || '-' || CAST("顧客ID" AS VARCHAR) AS japanese_orders
, 1 AS __order_count_ja
FROM dbt_sl_db.dbt_tyasuhara.stg_test_japanese stg_test_japanese_src_10000
) subq_3
GROUP BY
japanese_orders
LIMIT 100
派生メトリクス
派生メトリクスはメトリクスをそのまま参照するので、特に問題ありません。
- name: avg_tax_per_order_ja
type: derived
expr: tax_amount_ja / order_count_ja # メトリクス名(ASCII)を参照するため日本語制約なし
input_metrics:
- name: tax_amount_ja
- name: order_count_ja
検証内容のまとめ
検証した内容をまとめると以下の通りです。MetricFlow のセマンティック識別子(名前として使われるもの)と SQL 式(値として評価されるもの)で、日本語使用可否の制約が異なります。
| YAML のキー | 用途 | 日本語使用 |
|---|---|---|
columns: - name: |
物理カラムへの参照 + セマンティック識別子のデフォルト名 | 識別子として検証される |
entity: name: / dimension: name: |
MetricFlow のセマンティック識別子 | ASCII のみ(lowercase + underscore) |
agg_time_dimension: |
time dimension の name: を参照 |
ASCII のみ |
metrics: - name: |
メトリクス識別子 | ASCII のみ |
metrics: expr: |
SQL 式 | "金額"のように引用符付きで参照可能 |
derived_semantics: entities: expr: |
SQL 式(複合キー) | "金額"のように引用符付きで参照可能 |
Label の付与
label:で MetricFlow のセマンティック定義に表示名を付与できます。こちらであればname:(識別子)とは独立して設定でき、日本語も使えます。
metrics:
- name: order_count_ja
type: simple
label: 注文数
agg: count
expr: 1
- name: tax_amount_ja
type: simple
label: 消費税額
agg: sum
expr: '"金額" * 0.1'
- name: total_amount_ja
type: simple
label: 金額合計
agg: sum
expr: '"金額"'
- name: avg_tax_per_order_ja
type: derived
label: 注文あたり消費税額
expr: tax_amount_ja / order_count_ja
input_metrics:
- name: tax_amount_ja
- name: order_count_ja
下図は Steep の例ですが、対応する BI ツールであれば label で付与した名称でメトリクスやディメンションが表示されます。

さいごに
カラム名が日本語のテーブルに対して dbt Semantic Layer(MetricFlow)のセマンティックモデルを定義する方法を確認してみました。
こちらの内容がどなたかの参考になれば幸いです。







