数据与分析

语义层

度量只定义一次,处处复用。它的存在意义在于:两个人对同一份数据问同一个问题,不可能得到两个不同的答案。

打开 数据 → 语义层。你在自己的表之上定义 度量 (数值)与 维度 (切分它们的方式);平台会把对它们的请求编译成 SQL 并执行。

为什么这样设计

没有这一层,「营收」就是最后那个人写的 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。人和智能体都是按这个名字来选的。
  • 把排除项写进描述里。 「剔除退款与内部测试账号」这一句,就能挡掉这一层本来要终结的大部分争论。
  • 从争议最大的那几个开始。 五个人人有异议的度量,价值大于五十个没人看的。
  • 维度要有意识地声明。 你暴露的每个维度都会有人截图传播——那些让度量失去意义的维度,就别放出来。
  • 改定义等于改历史。 所有读取该度量的地方都会跟着变。请公告一下,并把改动记在描述里。

访问控制

度量的访问权继承自其底层的表——读不到源表的用户,用不了建立在它上面的度量。授权在此管理: 访问控制.