数据与分析
语义层
度量只定义一次,处处复用。它的存在意义在于:两个人对同一份数据问同一个问题,不可能得到两个不同的答案。
打开 数据 → 语义层。你在自己的表之上定义 度量 (数值)与 维度 (切分它们的方式);平台会把对它们的请求编译成 SQL 并执行。
为什么这样设计
组成部分——逐字段说明
度量
| 字段 | 必填 | 取值 / 说明 |
|---|---|---|
name | 是 | 稳定的标识符,需匹配 ^[a-zA-Z_][a-zA-Z0-9_]*$ ——它会成为 SQL 里的别名,所以不能有空格和连字符。 |
label | 否 | 供人阅读的显示名 |
description | 否 | 它是什么含义、排除了什么。智能体也会读这段。 |
agg | 是 | sum, avg, count, count_distinct, min, max, custom, derived |
sql | 取决于情况 | 要聚合的列或表达式。对 count可选;对 custom则必填,此时它是完整的聚合表达式,例如 SUM(revenue) - SUM(cost)。对 derived 它是一个基于其他度量的公式,其他度量以 {metric_name}, e.g. {revenue} / NULLIF({orders}, 0) 的形式引用——每个标记会被替换成该度量自己的表达式,所以一个比率总是随其分子分母的当前定义变化。派生度量可以引用其他派生度量;循环引用或未知引用会在编译期被拒绝。 |
filters | 否 | 布尔型 SQL 片段,以 AND 拼进聚合函数内部——即带筛选的度量,例如 status = 'paid'。当聚合方式为 custom. |
format | 否 | number | currency | percent |
currency | 否 | format 为 currency 时填 ISO 4217 代码 |
为什么这样设计
filters 落在聚合函数内部,而不是查询的 WHERE 子句里。这个区别正是关键所在: net_revenue 可以剔除退款,同时与 gross_revenue落在同一批行上,于是两者能出现在同一个结果里,而不会有一个悄悄把另一个也筛掉。维度
| 字段 | 必填 | 取值 / 说明 |
|---|---|---|
name | 是 | 标识符规则与度量相同——它就是 SQL 别名。 |
label | 否 | 显示名称 |
description | 否 | 这个切分维度的含义 |
sql | 是 | 一个列,或一个表达式,例如 DATE_TRUNC('month', created_at). |
type | 否 | 字段类型。 time 维度可以在查询时按 粒度 汇总(日 / 周 / 月 / 季 / 年)——编译器会按各数仓方言生成正确的截断函数,所以你只写一次原始列,就能按需拿到月度或季度分桶。本地数据集在默认引擎 DuckDB 上支持所有粒度;在 LOCAL_ENGINE=alasql 这条逃生通道上,除「周」以外的粒度都支持。 |
这些 SQL 字段是受信任的
sql 字段会原样插入编译后的查询里。只有你信任其对你的数仓写 SQL 的人,才应该来定义度量——这是一个创作界面,不是用户输入。定义一个度量
先选数据源—— 本地数据集 或 数仓表 (任意已接入的 Snowflake / BigQuery / Postgres 等连接;编辑器可浏览其中的表)——给度量命名、选好聚合方式与表达式、加上属于定义本身的筛选条件,并声明它可以按哪些维度切分。保存前编辑器会预览编译出的 SQL 和一份样本结果——请读那段 SQL,它就是定义。
验证 会编译每个维度与度量,并逐个在真实数据源上执行,按字段报告失败。保存前先用它:否则一个写错的列名要等到很久之后仪表板刷新时才以引擎错误的形式暴露出来。编辑器下方的 查询运行器 可以挑选度量与维度、添加筛选条件(维度筛选变成 WHERE,度量筛选变成 HAVING)和时间汇总,结果可以直接作为受治理的组件发送到仪表板。
SELECT date_trunc('month', o.created_at) AS month,
o.region AS region,
SUM(o.amount) AS net_revenue
FROM orders o
WHERE o.status = 'settled'
AND o.is_refund = false
GROUP BY 1, 2联接——跨越星型模型
一个模型最多可以声明八个从其源表出发的 LEFT/INNER 联接,这样维度和度量就能引用关联表(比如在customers.segment 事实表上引用 orders ),而不必先在视图或数据准备流程里预联接——度量定义本身就是完整说明。表名和别名会按严格标识符校验; ON 条件由模型所有者编写,信任级别与维度的 SQL 相同。一旦存在联接,片段里的列名就要带上限定前缀。
相对日期筛选
优先用这些,而不是写死日期:它们每次查询执行时都相对「今天」求值,所以仪表板不会因为时间推移而需要改动。 last_n_days (取值为天数), this_month, last_month, this_quarter, last_quarter 和 ytd。它们只能作用于 时间 类型的维度,且比较的是原始日期而不是汇总后的桶——所以「最近 30 天」按月分组之后仍然是 30 天。时间窗是半开区间、按 UTC 计算,运行器会显示每个窗口实际解析出的日期。
同比 / 环比
设置 compare 改为 yoy, mom 或 prior_period ,此后每个度量都会多出 _prev, _change 和 _pct_change (一个小数—— 0.25 表示 +25%)。它要求恰好有一个带粒度的时间维度:那就是被比较的轴。 prior_period 回退该粒度的一个单位, mom 回退一个月, yoy 则不论粒度都回退一年。
有三个行为值得知道。一个 没有前一期 的周期——序列里的第一个,或数据存在断档——会显示为空,而不是从结果里被剔除。 _pct_change 就是 当前一期的值为零时留空,因为从「无」变化而来算不出百分比。另外你设的任何日期筛选都会 随比较一起移动,所以筛到今年之后,比较的仍然是去年,而不是什么都没有。
在 AlaSQL 这条逃生通道上不可用(LOCAL_ENGINE=alasql),它既没有 CTE 也没有日期运算——编译器会带着这条说明拒绝,而不是生成一段自己跑不了的 SQL。
WITH semantic_cur AS (…), semantic_prev AS (… shifted one year …)
SELECT semantic_cur."month",
semantic_cur."net_revenue",
semantic_prev."net_revenue" AS "net_revenue_prev",
(semantic_cur."net_revenue" - semantic_prev."net_revenue") AS "net_revenue_change",
CASE WHEN semantic_prev."net_revenue" = 0 THEN NULL
ELSE (semantic_cur."net_revenue" - semantic_prev."net_revenue")
* 1.0 / semantic_prev."net_revenue" END AS "net_revenue_pct_change"
FROM semantic_cur
LEFT JOIN semantic_prev ON semantic_cur."month" IS NOT DISTINCT FROM semantic_prev."month"谁在用它
| 消费方 | 怎么用 |
|---|---|
| 仪表板 | 在构建面板里挑一个度量,而不是自己写查询。图块继承该定义,定义变了它也跟着变。 |
| 智能体可用 | 在任意已接入此知识库的智能体或集群节点上启用 metric_query 工具。智能体按名称索取度量,并给出维度和筛选条件——它绝不自己重算这个数字。 |
| 智能体对话 | 关于受治理数字的回答,会与展示同一度量的仪表板保持一致。 |
| BI 的 AI 分析师 | 当它为某个图表或 AI 生成的仪表板写 SQL 时,相关表的受治理定义会被注入它的上下文,并附上「必须用定义中的表达式来计算这些度量」的指令——于是临时 BI 分析与度量图块口径一致,而不是临场另编一个公式。 |
注意
该用度量还是直接写 SQL?
| 用度量的场景 | 用 SQL 的场景 |
|---|---|
| 这个数字会出现在不止一个地方 | 这是一次性的排查 |
| 大家会为它的定义争论 | 形态还在探索、经常改 |
| 可能会有人向智能体索要它 | 你需要这一层没有建模的联接或窗口函数 |
| 数据模型变化时它必须保持一致 | 你在做原型,之后会扔掉 |
实用建议
- 按业务命名,不要按表结构命名。
net_revenue,而不是sum_amt_filtered。人和智能体都是按这个名字来选的。 - 把排除项写进描述里。 「剔除退款与内部测试账号」这一句,就能挡掉这一层本来要终结的大部分争论。
- 从争议最大的那几个开始。 五个人人有异议的度量,价值大于五十个没人看的。
- 维度要有意识地声明。 你暴露的每个维度都会有人截图传播——那些让度量失去意义的维度,就别放出来。
- 改定义等于改历史。 所有读取该度量的地方都会跟着变。请公告一下,并把改动记在描述里。
访问控制
度量的访问权继承自其底层的表——读不到源表的用户,用不了建立在它上面的度量。授权在此管理: 访问控制.