-- ❌ 错误:虚拟列 is_completed_flag 在 GPT 视图中不可解析
SUM(is_completed_flag)
-- ✅ 正确:内联 CASE WHEN
SUM(CASE WHEN order_status = 'completed' THEN 1 ELSE 0 END)
不要使用相关子查询。GPT 视图不支持相关子查询的外层引用,会导致静默错误:
-- ❌ 错误:GPT 视图中外层引用丢失,返回错误值但不报错
CASE WHEN (SELECT COUNT(*) FROM v_gpt_fact_order fo2
WHERE fo2.member_key = v.member_key) = 1 THEN ...
-- ✅ 正确:改用答案构建器,用 GROUP BY 实现
⚠️ 注意:经验法则——如果在指标表达式里写子查询或窗口函数,停下来,改用答案构建器实现。
完整示例
以下指标代表典型的最佳用法:
GMV
表达式: ROUND(SUM(CASE WHEN order_status='completed' THEN gross_amount ELSE 0 END), 2)
别名: "总销售额", "营收", "挂牌GMV"
说明: 已完成订单的挂牌金额总额。单表(fact_order),纯聚合,无子查询。
客单价
表达式: ROUND(SUM(CASE WHEN order_status='completed' THEN net_amount ELSE 0 END)
/ NULLIF(SUM(CASE WHEN order_status='completed' THEN 1 ELSE 0 END), 0), 2)
说明: 分子分母都加了过滤,NULLIF 防除零。
单均配送成本
表达式: ROUND(SUM(CASE WHEN fulfillment_type='delivery' AND order_status='completed'
THEN delivery_fee_cost ELSE 0 END)
/ NULLIF(SUM(CASE WHEN fulfillment_type='delivery' AND order_status='completed'
THEN 1 ELSE 0 END), 0), 2)
说明: 同时过滤 fulfillment_type 和 order_status。
✅ SELECT ${dims}, agg1, agg2 FROM ... GROUP BY ${dims}
✅ SELECT * FROM (SELECT ${dims}, agg1 FROM ... GROUP BY ${dims}) t
❌ SELECT ${dims}, total FROM (SELECT ch AS channel, COUNT(*) AS total FROM ...) t
→ 跨子查询边界列重命名后解析失败
窗口函数:放子查询内,外层
SELECT *
SELECT *
包裹:
SELECT * FROM (
SELECT ${dims}, COUNT(*) AS cnt,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) AS rnk
FROM ... GROUP BY ${dims}
) t
CTE:
WITH t AS (
SELECT ${dims}, COUNT(*) AS cnt FROM ... GROUP BY ${dims}
) SELECT * FROM t
SQL 典型样例
以下样例覆盖从单表到多表 JOIN 的常见复杂度梯度。
样例一:单表 + filter(最简模式)
场景:按渠道和履约类型统计订单数和 GMV,可过滤订单状态。
SELECT ${dims},
COUNT(DISTINCT fo.order_id) AS orders,
ROUND(SUM(CASE WHEN fo.order_status =
chr(99)||chr(111)||chr(109)||chr(112)||chr(108)||chr(101)||chr(116)||chr(101)||chr(100)
THEN fo.net_amount ELSE 0 END), 2) AS gmv
FROM sales_demo.v_gpt_fact_order fo
WHERE ${filters}
GROUP BY ${dims}
对应 chartParams:dims 可选
channel
channel
、
fulfillment_type
fulfillment_type
、
pay_method
pay_method
;filters 可选
order_status
order_status
、
channel
channel
。
样例二:两表 JOIN + 维度下钻
场景:按门店维度(名称/城市/商圈/形态)统计 GMV 和订单数。
SELECT ${dims},
COUNT(DISTINCT fo.order_id) AS orders,
ROUND(SUM(CASE WHEN fo.order_status =
chr(99)||chr(111)||chr(109)||chr(112)||chr(108)||chr(101)||chr(116)||chr(101)||chr(100)
THEN fo.net_amount ELSE 0 END), 2) AS gmv
FROM sales_demo.v_gpt_dim_store ds
JOIN sales_demo.v_gpt_fact_order fo
ON ds.store_key = fo.store_key
GROUP BY ${dims}
对应 chartParams:dims 可选
store_name
store_name
、
city
city
、
city_tier
city_tier
、
trade_zone_type
trade_zone_type
、
store_format
store_format
。
关键点:维度列来自 dim_store,度量来自 fact_order——跨表维度下钻的典型模式。
样例三:三表 JOIN(订单→明细→商品)
场景:按商品品类和日期统计销量和 GMV。
SELECT ${dims},
SUM(foi.quantity) AS cups,
ROUND(SUM(foi.item_gross_amount - foi.item_discount_amount), 2) AS revenue
FROM sales_demo.v_gpt_fact_order fo
JOIN sales_demo.v_gpt_fact_order_item foi
ON fo.order_id = foi.order_id
JOIN sales_demo.v_gpt_dim_sku ds
ON foi.sku_key = ds.sku_key
WHERE fo.order_status =
chr(99)||chr(111)||chr(109)||chr(112)||chr(108)||chr(101)||chr(116)||chr(101)||chr(100)
GROUP BY ${dims}
不能和 ROW_NUMBER 的 PARTITION BY 共享同一列,将窗口函数放在内层子查询,外层用
SELECT *
SELECT *
包裹。
SELECT * FROM (
SELECT ${dims},
COUNT(DISTINCT order_id) AS orders,
ROW_NUMBER() OVER (ORDER BY COUNT(DISTINCT order_id) DESC) AS rank
FROM sales_demo.v_gpt_fact_order fo
GROUP BY ${dims}
) t
对应 chartParams:dims 可选
channel
channel
、
fulfillment_type
fulfillment_type
、
order_status
order_status
。
关键点:①窗口函数放在内层子查询,外层
SELECT *
SELECT *
包裹——这是 AB 中使用窗口函数的唯一可靠模式;②
${dims}
${dims}
在内层直接用于 GROUP BY 和 SELECT,与 ROW_NUMBER 在同一作用域。
⚠️ 注意:如果需要对
${dims}
${dims}
之外的列做 PARTITION BY(如「按城市层级分组的门店排名」),由于 PARTITION BY 的列不能同时出现在
${dims}
${dims}
的 GROUP BY 中,这种场景超出当前 AB 模板能力——建议拆分为两个 AB(一个汇总、一个排名),或在 Agent 对话中由 LLM 自行生成。
样例五:INNER JOIN 子查询——会员预聚合
场景:按会员等级分析复购率——先用子查询统计每会员订单数,再 JOIN 会员维度。
SELECT ${dims},
COUNT(DISTINCT m.member_key) AS total_members,
COUNT(DISTINCT CASE WHEN o.order_cnt >= 2
THEN m.member_key END) AS repurchase_members,
ROUND(COUNT(DISTINCT CASE WHEN o.order_cnt >= 2
THEN m.member_key END) * 100.0
/ NULLIF(COUNT(DISTINCT m.member_key), 0), 2) AS repurchase_rate
FROM sales_demo.v_gpt_dim_member m
JOIN (SELECT member_key,
COUNT(*) AS order_cnt
FROM sales_demo.v_gpt_fact_order
WHERE order_status =
chr(99)||chr(111)||chr(109)||chr(112)||chr(108)||chr(101)||chr(116)||chr(101)||chr(100)
GROUP BY member_key) o
ON m.member_key = o.member_key
GROUP BY ${dims}
对应 chartParams:dims 可选
member_tier
member_tier
、
register_channel
register_channel
、
city
city
。
关键点:①用 INNER JOIN(而非 LEFT JOIN)确保分母为「有购买记录的会员」;②子查询只做预聚合,不引用
${dims}
${dims}
——dims 在外层直接来自 dim_member;③这是替代 GPT 视图相关子查询的标准模式。
样例六:事实表 + 多维度表联查——断货分析
场景:按商品和月份分析断货天数、断货率和缺货时长。
SELECT ${dims},
SUM(CASE WHEN fss.stockout_flag = 1
THEN 1 ELSE 0 END) AS stockout_days,
ROUND(SUM(CASE WHEN fss.stockout_flag = 1
THEN 1.0 ELSE 0 END) / COUNT(*) * 100, 2) AS stockout_rate,
SUM(CASE WHEN fss.stockout_flag = 1
THEN fss.stockout_hours ELSE 0 END) AS stockout_hours
FROM sales_demo.v_gpt_fact_store_stockout fss
JOIN sales_demo.v_gpt_dim_sku ds
ON fss.sku_key = ds.sku_key
JOIN sales_demo.v_gpt_dim_date dd
ON fss.date_key = dd.date_key
GROUP BY ${dims}
# 遍历域中所有表的列语义
for dsid in $(cz-cli analytics-agent domain detail <domain-id> --with-tables --format json | \
python3 -c "import sys,json; [print(t['datasetId']) for t in json.load(sys.stdin)['data']['tables']]"); do
cz-cli analytics-agent table columns $dsid --format json
done
提取每个列的关键字段:
attrCode
attrCode
(列名)、
semanticType
semanticType
(类型)、
description
description
(描述)、
intendedTypes
intendedTypes
(用途)。
跨表同名列检测
将全部列名去重后,找出在多张表中重复出现的列名:
columns_by_table = {table: {col['attrCode'] for col in cols} for table, cols in scan_result}
all_cols = set.union(*columns_by_table.values())
duplicates = {c for c in all_cols if sum(1 for cols in columns_by_table.values() if c in cols) > 1}
逐组对比语义
对每组同名列,对比其
description
description
,判断是同义(用于 JOIN)还是异义(需要消歧):
判断
示例
处理
同义(JOIN 键)
member_key
member_key
在 dim_member 和 fact_order 中
无需处理
异义(业务含义不同)
city
city
在 dim_store(门店城市)和 dim_member(会员常驻城市)
需消歧
粒度混淆
discount_amount
discount_amount
在 fact_order(订单级)和 order_item(SKU 级)
需标注
数据验证
对异义列,查询不同 JOIN 路径是否产生不同结果:
-- 路径A:门店城市(正确)
SELECT ROUND(SUM(CASE WHEN fo.order_status='completed' THEN fo.net_amount ELSE 0 END),0) AS gmv
FROM fact_order fo JOIN dim_store ds ON fo.store_key = ds.store_key
WHERE ds.city = '上海';
-- 路径B:会员城市(错误)
SELECT ROUND(SUM(CASE WHEN fo.order_status='completed' THEN fo.net_amount ELSE 0 END),0) AS gmv
FROM fact_order fo JOIN dim_member dm ON fo.member_key = dm.member_key
WHERE dm.city = '上海';
-- 如果两值不同且差值 > 1%,确认风险
-- 检测死维度
SELECT 'product_family' AS col,
COUNT(DISTINCT product_family) AS distinct_vals,
ROUND(SUM(CASE WHEN product_family IS NULL THEN 1.0 ELSE 0 END)/COUNT(*)*100, 0) AS null_pct
FROM dim_sku;
-- 若 distinct=1 且 null_pct=95 → 死维度,应 hidden=true
SELECT channel, COUNT(*) FROM fact_order GROUP BY channel;
SELECT issue_channel, COUNT(*) FROM fact_coupon GROUP BY issue_channel;
-- 若发现 fact_order 有「抖音团购核销」、fact_coupon 有「抖音直播间团购」→ 需统一枚举值
-- 对应 Agent 回答「总体复购率 92.19%」
WITH member_orders AS (
SELECT m.member_key, m.member_tier,
COUNT(fo.order_id) AS order_cnt
FROM dim_member m
JOIN fact_order fo ON m.member_key = fo.member_key
AND fo.order_status = 'completed'
GROUP BY m.member_key, m.member_tier
)
SELECT member_tier,
COUNT(DISTINCT member_key) AS total,
COUNT(DISTINCT CASE WHEN order_cnt >= 2 THEN member_key END) AS repurchase,
ROUND(100.0 * COUNT(DISTINCT CASE WHEN order_cnt >= 2 THEN member_key END)
/ NULLIF(COUNT(DISTINCT member_key), 0), 2) AS rate
FROM member_orders
GROUP BY member_tier
ORDER BY rate;