跳到主要内容
版本:26.x

语义模型概念

SelectDB MCP 服务通过 Model Context Protocol (MCP) 让 AI Agent 直接查询 SelectDB Cloud 中的数据。它提供两条查询路径:

  • 有语义模型:语义查询。当 healthy workspace 中存在包含匹配指标的语义模型时,Agent 优先使用该定义,MetricFlow 生成 SQL。
  • 没有语义模型:SQL 查询。当没有语义模型或匹配指标时,Agent 发现元数据并生成只读 SQL 查询。

语义路径的准确性取决于模型定义本身。正确的指标表达式、维度、关系和过滤条件才能产生一致、受治理的结果。定义不正确时,即使查询成功,也可能返回错误答案。

本页介绍语义模型的概念和 YAML 结构。要在 Semantic Web UI 中创建、校验、提交或排查模型问题,请看创建和管理语义模型

什么是语义模型?

语义模型是对数据库表的业务描述。你告诉系统:

  • 这张表的主键是什么(实体)

  • 哪些字段可以用来分组(维度)

  • 哪些字段需要聚合(度量)

定义完成后,用户就可以用自然语言查询(如"显示每月订单总金额"),系统会自动生成正确的 SQL。

提示:

YAML 是规范,MetricFlow 是翻译器:它把"每月订单总金额"翻译成 SELECT DATE_TRUNC('month', order_date), SUM(amount) FROM orders GROUP BY 1

语义模型结构

一个完整的 semantic_model 包含以下字段:

字段类型必填说明
namestring全局唯一的模型名称。以小写字母开头,可包含数字和下划线
db_tablestringSelectDB 物理表,格式为 database.table,例如 dw.orders
defaultsobject默认配置。当前必须包含 agg_time_dimension
entitieslist表的实体定义。至少需要一个 type: primary 主实体
dimensionslist用于分组和过滤的维度
measureslist推荐聚合定义。定义后会自动成为可查询的指标
descriptionstring可选模型的文字描述
labelstring可选显示名称
primary_entitystring条件必填entities 中没有 type: primary 实体时必填

警告:

所有名称(模型、实体、维度、度量)必须满足:

  • 以小写字母开头
  • 只包含小写字母、数字和下划线
  • 不能包含连续两个下划线 __
  • 长度至少为 2 个字符

order_idtotal_amountuser_count

OrderIDorder__ida

实体与表身份

实体定义行之间的唯一性和关系。每张表必须有一个主实体

字段类型必填说明
namestring实体名称,在该模型内唯一
typeenum实体类型(见下表)
exprstring推荐对应的数据库列。可以省略(默认为 name);也支持 SQL 表达式
descriptionstring可选文字描述
labelstring可选显示名称

实体类型

类型含义使用场景
primary主键。每行唯一,覆盖所有记录表的 ID 列。每张表必须恰好有一个主实体
foreign外键。允许重复和空值关联其他表的列,如 customer_idproduct_id
unique唯一键。每行唯一,可能不覆盖所有记录如邮箱、身份证号
natural自然键。现实世界中的唯一标识如商品条码、员工工号

示例:orders 表的实体定义

entities:
- name: order_id # 主键:每个订单的唯一 ID
type: primary
expr: order_id

- name: customer # 外键:关联 users 表
type: foreign
expr: user_id

- name: order_ref # 外键 + SQL 表达式
type: foreign
expr: substring(trace_id FROM 1 FOR 8)

注意:

如果表没有 type: primary 实体,请在模型顶层使用 primary_entity: entity_name

用于分组数据的维度

维度定义数据如何分组和过滤。有两种类型:时间维度分类维度

字段类型必填说明
namestring维度名称
typeenumtimecategorical
type_paramsobject时间维度必填时间粒度配置(见下文)
exprstring推荐对应的列或 SQL 表达式
is_partitionbool可选是否为分区列。默认 false
descriptionstring可选文字描述
labelstring可选显示名称

时间粒度(time_granularity

粒度含义示例
day按天2025-01-15
week按周2025-W03
month按月2025-01
quarter按季度2025-Q1
year按年2025
hour按小时2025-01-15 14:00
minute按分钟2025-01-15 14:30

示例

dimensions:
- name: order_date # 下单日期(按天)
type: time
type_params:
time_granularity: day
expr: order_date

- name: order_month # 下单月份(按月)
type: time
type_params:
time_granularity: month
expr: order_date # 同一列,不同粒度

- name: channel # 渠道(分类)
type: categorical
expr: channel

- name: status_label # 使用 SQL 表达式
type: categorical
expr: concat(status, '_', channel)

用于聚合的度量

度量定义对数据列的聚合。每个度量会自动生成一个同名可查询指标

字段类型必填说明
namestring度量名称(同时成为指标名称)
aggenum聚合类型(见下表)
exprstring推荐要聚合的列或 SQL 表达式
descriptionstring可选指标描述
labelstring可选显示名称
create_metricbool可选设为 false 时不自动生成指标。默认 true
agg_time_dimensionstring可选覆盖模型默认的时间维度

聚合类型

类型含义典型用途
sum求和总金额、总数量
count计数订单数、用户数
count_distinct去重计数去重用户数、活跃设备数
average平均平均金额、平均时长
min最小最低价格、最早时间
max最大最高价格、最晚时间
median中位数订单金额中位数
percentile百分位P99 延迟、P95 金额(需要 agg_params.percentile
sum_boolean布尔求和转化数、通过数

示例

measures:
- name: total_amount # 订单总金额
description: "Sum of all order amounts"
agg: sum
expr: amount
label: "Total Amount"

- name: order_count # 订单数
agg: count
expr: order_id

- name: unique_customers # 去重客户数
description: "Number of distinct customers who placed an order"
agg: count_distinct
expr: user_id

- name: p99_amount # P99 订单金额
agg: percentile
expr: amount
agg_params:
percentile: 0.99

- name: internal_counter # 不暴露为指标
agg: sum
expr: raw_value
create_metric: false

完整示例

下面是一个电商场景的完整语义模型定义——orders 表

# models/orders.yaml — orders 事实表
---
semantic_model:
name: orders
description: "E-commerce order fact table; each row is one order"
db_table: dw.orders
defaults:
agg_time_dimension: order_date

# ── 实体 ──
entities:
- name: order_id
description: "Order primary key"
type: primary
expr: order_id

- name: customer
description: "Associated user"
type: foreign
expr: user_id

- name: product
description: "Associated product"
type: foreign
expr: product_id

# ── 维度 ──
dimensions:
- name: order_date
description: "Order date (by day)"
type: time
type_params:
time_granularity: day
expr: order_date

- name: order_month
description: "Order month"
type: time
type_params:
time_granularity: month
expr: order_date

- name: channel
description: "Order channel"
type: categorical
expr: channel

- name: status
description: "Order status"
type: categorical
expr: status

# ── 度量 ──
measures:
- name: total_amount
description: "Total order amount"
label: "Total Amount"
agg: sum
expr: amount

- name: order_count
description: "Total order count"
label: "Order Count"
agg: count
expr: order_id

- name: unique_customers
description: "Distinct customers who placed an order"
label: "Distinct Customers"
agg: count_distinct
expr: user_id

- name: avg_amount
description: "Average order amount"
label: "Average Order Value"
agg: average
expr: amount

配套:users 和 products 表

# models/users.yaml — users 维度表
---
semantic_model:
name: users
description: "User dimension table"
db_table: dw.users
defaults:
agg_time_dimension: register_date

entities:
- name: user_id
type: primary
expr: user_id

dimensions:
- name: register_date
type: time
type_params:
time_granularity: day
- name: city
type: categorical
- name: level
type: categorical

measures:
- name: user_count
agg: count
expr: user_id
# models/products.yaml — products 维度表(纯维度表,没有度量,因此省略 defaults)
---
semantic_model:
name: products
description: "Product dimension table"
db_table: dw.products

entities:
- name: product
type: primary
expr: product_id

dimensions:
- name: product_name
type: categorical
expr: name
- name: category
type: categorical
expr: category
- name: brand
type: categorical
expr: brand

高级指标定义

除了由 measures 自动生成的简单指标,你还可以用 metric: 文档定义高级指标。高级指标组合已有的度量或指标,实现更复杂的逻辑。支持四种类型:

类型含义典型场景
ratio比率指标:分子 ÷ 分母转化率、利润率、占比
derived派生指标:基于已有指标的表达式环比增长、同比增长、加权计算
cumulative累计指标:在时间窗口内累加最近 7 天销售额、月度累计注册数
conversion转化指标:两个事件之间的转化分析下单转化率、注册转化率

比率指标

计算两个指标的比率,例如人均订单数 = 订单数 / 用户数。

# models/orders_per_user.yaml
---
metric:
name: orders_per_user
description: "Orders per user: order count / user count"
type: ratio
type_params:
numerator: order_count # 分子:引用已有指标
denominator: user_count # 分母:引用已有指标

分子和分母都必须是已经定义好的指标名称(简单指标或其他高级指标均可)。

派生指标

通过表达式基于一个或多个已有指标计算。最常用于环比和同比计算。

# models/revenue_growth.yaml — 环比增长
---
metric:
name: revenue_growth
description: "Revenue period-over-period growth rate"
type: derived
type_params:
expr: (current_revenue - prev_revenue) / prev_revenue
metrics:
- name: total_amount # 本期营收
alias: current_revenue
- name: total_amount # 上期营收(同一指标,带时间偏移)
alias: prev_revenue
offset_window: 1 month # 向前偏移一个时间窗口
参数说明
expr计算表达式;通过 alias 引用每个输入指标
metrics输入指标列表
name被引用的指标名称
aliasexpr 中使用的别名
offset_window时间偏移,如 1 month7 days1 year
offset_to_grain偏移的粒度,如 monthyear

累计指标

在时间窗口内累加某个指标,例如"最近 7 天销售额"。

# models/weekly_sales.yaml
---
metric:
name: weekly_sales
description: "Sales over the last 7 days"
type: cumulative
type_params:
measure:
name: total_amount
window: 7 days # 时间窗口:过去 7 天
参数说明
measure被引用的度量名称(来自 semantic_model 的 measures)
window时间窗口格式 数字 粒度,如 28 days4 weeks3 months
grain_to_date可选。累计到指定粒度,如 month(月初至今)、year(年初至今)

转化指标

衡量用户从一个事件(基础事件)转化到另一个事件(转化事件)的比率。常用于分析用户漏斗

# models/order_conversion.yaml
---
metric:
name: register_to_order_conversion
description: "Registration-to-order conversion rate"
type: conversion
type_params:
conversion_type_params:
base_measure: # 基础事件(注册)
name: user_count
conversion_measure: # 转化事件(下单)
name: order_count
entity: user # 关联实体:按哪个维度计算转化
calculation: conversion_rate # 计算方法
参数说明
base_measure基础事件的度量名称
conversion_measure转化事件的度量名称
entity计算转化所依据的关联实体(通常是 user 或 session)
calculationconversion_rate(转化率)或 conversions(绝对数量)
window可选。转化窗口,如 7 days

常见场景

场景 1:同一列定义不同粒度的维度

一个日期列可以同时定义天、周、月维度:

dimensions:
- name: order_date
type: time
type_params:
time_granularity: day
expr: order_date

- name: order_week
type: time
type_params:
time_granularity: week
expr: order_date # 同一列!

- name: order_month
type: time
type_params:
time_granularity: month
expr: order_date # 同一列!

场景 2:使用 SQL 表达式

当列名不够直观,或需要计算字段时,可以使用 SQL 表达式:

dimensions:
- name: user_label
type: categorical
expr: concat(level, '_', city) # 拼接字段

entities:
- name: user_short_id
type: foreign
expr: substring(trace_id FROM 1 FOR 8) # 子串

measures:
- name: net_amount
agg: sum
expr: coalesce(amount, 0) - coalesce(discount, 0) # 计算字段

提示:

支持 substringconcatcoalescecast 等标准 SQL 函数。这些表达式在物理校验阶段会被识别,跳过列名校验。

场景 3:分区表

如果表有分区列,用 is_partition: true 标记:

dimensions:
- name: ds
type: time
type_params:
time_granularity: day
is_partition: true # 标记为分区列
expr: ds

场景 4:隐藏内部度量

有些度量只是中间计算,不应暴露给终端用户,可设置 create_metric: false

measures:
- name: total_amount # ✅ 公开指标
agg: sum
expr: amount

- name: _raw_count # ❌ 不暴露
agg: count
expr: order_id
create_metric: false

高级功能

过滤条件

可以在度量定义指标定义中添加 SQL 过滤条件。过滤在聚合之前生效。

注意:

可以在以下位置添加过滤条件:

  • measures 条目的 filter: 字段:限定单个度量的数据范围。
  • metric:filter: 字段:限定整个指标的数据范围。
  • 度量被高级指标引用时,input_measures[].filter: 限定被引用的度量。
# 示例 1:度量级过滤——只统计"已完成"订单的总金额
measures:
- name: completed_amount
description: "Sum of completed order amounts"
agg: sum
expr: amount
filter: {{ render_dimension_template('status') }} = 'completed'
# 示例 2:指标级过滤
---
metric:
name: premium_user_orders
description: "Order count for premium users"
type: simple
type_params:
measure:
name: order_count
filter: {{ render_dimension_template('user_level') }} = 'premium'

警告:

过滤条件语法要求:

  • 在 YAML 中用 {{ Dimension('qualified_name') }}{{ render_dimension_template('dimension_name') }} 引用维度。
  • {{ Entity('entity_name') }}{{ render_entity_template('entity_name') }} 引用实体。
  • 引用后跟正常的 SQL 条件,如 = 'value'IN ('a', 'b')
  • query_metricwhere 参数直接接受原始 SQL,如 "channel = 'APP'"。编译器会自动转换为 MetricFlow 模板语法。

保存的查询

把常用的指标 + 分组 + 过滤组合保存为查询模板,用户可以一键调用。

# models/weekly_report.yaml
---
saved_query:
name: weekly_revenue_report
description: "Weekly revenue report: total order amount and order count grouped by channel"
label: "Weekly Revenue Report"
query_params:
metrics:
- total_amount
- order_count
group_by:
- order_id__order_week # 按周分组
- order_id__channel # 按渠道分组
order_by:
- "-order_id__order_week" # 按周倒序
limit: 52
字段说明
metrics要查询的指标名称列表
group_by分组维度,格式为 entity_name__dimension_name(双下划线连接)
order_by排序;- 前缀表示倒序
where过滤条件(语法与过滤条件相同)
limit最大返回行数

提示:

用双下划线 entity_name__dimension_name 引用维度。例如 order_id__order_date 表示 orders 表的 order_date 维度。

不可加度量和缓慢变化维度(SCD Type II)

有些度量不能简单相加(如库存、账户余额),需要按特定维度取快照值。

# 不可加度量:库存(月末快照)
measures:
- name: monthly_inventory
description: "Month-end inventory"
agg: sum
expr: inventory_count
non_additive_dimension:
name: snapshot_date # 不可加维度
window_choice: max # 在时间窗口内取最大值
window_groupings:
- product # 按产品分组取快照

SCD Type II(缓慢变化维度):当维度表有生效时间范围时,标记开始和结束维度:

# 在维度表中标记 SCD Type II
dimensions:
- name: valid_from
description: "Validity start time"
type: time
type_params:
time_granularity: day
validity_params:
is_start: true # 标记为开始时间

- name: valid_to
description: "Validity end time"
type: time
type_params:
time_granularity: day
validity_params:
is_end: true # 标记为结束时间

空值填充和时间线对齐

# 把 NULL 替换为 0(让计数类指标在没有数据的日期显示 0)
metric:
name: daily_orders
type: simple
type_params:
measure:
name: order_count
fill_nulls_with: 0 # 在没有数据的日期显示 0
join_to_timespine: true # 对齐时间线(补齐缺失日期)
参数说明
fill_nulls_with把聚合结果中的 NULL 替换为指定值(通常为 0)
join_to_timespine把指标结果与时间线表关联,使每天/每月/每年都有行(缺失日期填充 NULL 或 0)

原生表引用格式

除了 db_table 简写,还支持 MetricFlow 原生的 node_relation 格式,包括三段式 catalog 引用:

# 两段式:database.table
node_relation:
schema_name: dw
alias: orders

# 三段式:catalog.database.table
node_relation:
database: catalog
schema_name: dw
alias: orders

# db_table 也支持三段式
db_table: catalog.dw.orders

累计指标的聚合模式

累计指标支持三种 period_agg 模式,控制时间窗口内的聚合方式:

# last:取窗口内最后一天的值(默认行为)
metric:
name: end_of_week_inventory
type: cumulative
type_params:
cumulative_type_params:
measure:
name: inventory_count
window: 7 days
period_agg: last # 取最后一天的值

# average:窗口内的日均值
period_agg: average # 7 天平均值

# first:窗口内第一天的值
period_agg: first # 取第一天的值
period_agg说明
last窗口内最后一天的值(默认)
average窗口内的日均值
first窗口内第一天的值

转化指标的常量属性

在转化指标中,可以用 constant_properties 指定两个事件之间必须保持不变的属性:

# 按"流量来源"分析转化,要求两个事件来源相同
metric:
name: register_to_order_by_source
type: conversion
type_params:
conversion_type_params:
base_measure:
name: user_count
conversion_measure:
name: order_count
entity: user
constant_properties:
- base_property: order_id__channel # 基础事件的属性
conversion_property: order_id__channel # 转化事件的属性(必须匹配)

指标时间粒度和偏移粒度

指标级 time_granularity:可以直接在指标定义中设置时间粒度(覆盖查询时的默认值):

metric:
name: monthly_revenue
type: simple
time_granularity: month # 该指标默认按月聚合
type_params:
measure:
name: total_amount

offset_to_grain:在派生指标中,把偏移对齐到指定粒度(而不是默认的天粒度):

metric:
name: yoy_growth
type: derived
type_params:
expr: (current - prev) / prev
metrics:
- name: total_amount
alias: current
- name: total_amount
alias: prev
offset_window: 1 year
offset_to_grain: month # 偏移到月粒度(而不是天)

实体角色

同一个实体(如 user_id)在表中可能扮演多个角色。例如 orders 表中的 user_id 既是"买家"也是"推荐人"。用 role 区分:

entities:
- name: user
type: foreign
expr: buyer_id
role: buyer # 该 user 实体的角色是"买家"

- name: user
type: foreign
expr: referrer_id
role: referrer # 该 user 实体的角色是"推荐人"

未指定 role 时,默认角色等于实体名称。

最佳实践

  1. 每张表一个文件。 文件命名为 table_name.yaml,结构清晰、易于维护。

    orders.yamlusers.yamlproducts.yaml

  2. 先定义实体,再定义维度,最后定义度量。 实体是语义模型的骨架,维度提供分组能力,度量是查询目标。按这个顺序编写有助于避免遗漏。

  3. 为每个度量写 description 终端用户看到的是指标名称和描述。好的描述让用户不用读 YAML 就能理解指标。

  4. 同一个时间列可以定义多个粒度维度。 例如 order_date 列可以同时有天、周、月、季度、年五种粒度。

  5. 外键实体使用有业务含义的名称。 实体名 customeruser_id 更容易理解——它代表"客户"这个概念,而不只是"user_id 列"。

  6. 提交前先校验。 每次 YAML 变更后,按照校验与提交工作流操作。服务会检查表是否存在、列名是否正确以及命名规则。校验通过后再提交。

  7. 度量名称在模型内必须唯一。 度量名称可以跨模型重复,但会造成指标覆盖,所以最好保持全局唯一。

下一步

  • 创建和管理语义模型:在 Semantic Web UI 中创建工作区、添加模型文件、校验变更并提交。
  • 快速开始:把 SelectDB MCP 服务接入 AI Agent 并完成第一次查询。