Install
openclaw skills install text-to-sql-query直接通过 Text-to-SQL 方式查询零售数据库。根据用户自然语言描述,生成 SQL 查询语句并执行。 本 Skill 不依赖语义层或指标平台,而是直接基于数据库 schema 生成 SQL。 触发场景:用户需要查询零售数据、生成 SQL 查询、分析销售/客户/商品数据时使用。
openclaw skills install text-to-sql-query根据用户自然语言描述,直接生成 SQL 查询语句,通过 Gateway JDBC SQL 直查接口在零售数据库上执行并返回结果。
如果你不确定自己属于哪个类别,请按"标准模型"模式执行。
通过 Gateway JDBC SQL 直查接口执行 SQL 查询。API Key 通过环境变量 $CAN_API_KEY 注入,禁止在 Skill 文件中硬编码。
POST https://gateway.can.aloudata.com/api/jdbc/query
Content-Type: application/json
X-API-Key: $CAN_API_KEY
请求体:
{"sql": "SELECT ... FROM table_name WHERE ... LIMIT N"}
方式一:curl(推荐,适合 Bash 环境)
curl --noproxy '*' -s \
-H "X-API-Key: $CAN_API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://gateway.can.aloudata.com/api/jdbc/query" \
-d '{"sql": "YOUR SQL HERE"}'
方式二:Python requests
import os, requests
response = requests.post(
"https://gateway.can.aloudata.com/api/jdbc/query",
headers={
"X-API-Key": os.environ["CAN_API_KEY"],
"Content-Type": "application/json"
},
json={"sql": "YOUR SQL HERE"}
)
print(response.text) # 返回 Markdown 表格(纯文本)
多行 SQL 示例(curl heredoc):
curl --noproxy '*' -s \
-H "X-API-Key: $CAN_API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://gateway.can.aloudata.com/api/jdbc/query" \
-d @- <<'EOF'
{"sql": "SELECT o.dt, o.order_number, p.style_name, s.shop_name, o.retail_amount FROM fact_orders o JOIN dim_product p ON o.sku = p.sku JOIN dim_shop s ON o.shop_code = s.tr_shop_code WHERE o.dt >= '2024-01-01' ORDER BY o.dt DESC LIMIT 20"}
EOF
成功时返回 Markdown 表格(纯文本,非 JSON):
| sku | style_name | color_name | tag_price |
| --- | --- | --- | --- |
| SKU1000211 | 泡泡袖衬衫春秋款 | 白色 | 118.8600 |
| SKU1000222 | 背带裤明星同款 | 灰色 | 439.1300 |
2 row(s) returned.
错误时返回纯文本错误信息:
SQL validation error: Table not allowed: xxx — 查询了白名单以外的表SQL validation error: Only SELECT statements are allowed — 非 SELECT 语句Query error: Query timed out after 30 seconds — 查询超时Table not allowed 错误本数据库采用星型模型,包含 3 张事实表 + 3 张维度表。
| 表名 | 类型 | 说明 |
|---|---|---|
| fact_orders | 事实表 | 订单明细,每行一笔订单行项目 |
| fact_inventory | 事实表 | 库存快照,每行一个 SKU 在某门店/仓库的库存状态 |
| fact_product_launch | 事实表 | 商品上市记录,每行一个 SKU 在某门店的上市信息 |
| dim_product | 维度表 | 商品维度,SKU 级商品属性 |
| dim_shop | 维度表 | 店铺维度,门店属性及地理信息 |
| dim_vip | 维度表 | 会员维度,会员属性信息 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| pay_time | varchar | 支付时间 |
| delivery_date | varchar | 发货日期 |
| dt | date | 数据日期(分区键,用于时间筛选) |
| sku | varchar | SKU 编码(关联 dim_product) |
| shop_code | varchar | 接单门店编码(关联 dim_shop.tr_shop_code) |
| delivery_shop_code | varchar | 发货门店编码 |
| vip_id | bigint | 会员 ID(关联 dim_vip) |
| vip_code | varchar | 会员编码 |
| seller_id | varchar | 导购 ID |
| seller_code | varchar | 导购编码 |
| seller_name | varchar | 导购姓名 |
| warehouse_code | varchar | 仓库编码 |
| order_number | varchar | 订单号 |
| source_system_code | varchar | 来源系统编码 |
| order_platform_code | varchar | 订单平台编码 |
| retail_amount | decimal(38,9) | 零售金额(吊牌价口径) |
| retail_quantity | bigint | 零售数量 |
| retail_market_value | decimal(38,9) | 零售市值(吊牌额) |
| return_amount | decimal(38,9) | 退货金额 |
| return_quantity | bigint | 退货数量 |
| return_market_value | decimal(38,9) | 退货市值 |
| net_market_value | decimal(38,9) | 净市值 |
| net_quantity | bigint | 净数量(零售数量 - 退货数量) |
| net_amount | decimal(38,9) | 净金额(零售金额 - 退货金额) |
| unshipped_refund_quantity | bigint | 未发货退款数量 |
| unshipped_refund_amount | decimal(38,9) | 未发货退款金额 |
| shipped_refund_amount | decimal(38,9) | 已发货退款金额 |
| return_refund_quantity | bigint | 退货退款数量 |
| return_refund_amount | decimal(38,9) | 退货退款金额 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| unit_code | varchar | 单位编码 |
| unit_name | varchar | 单位名称 |
| warehouse_code | varchar | 仓库编码 |
| warehouse_name | varchar | 仓库名称 |
| shop_code | varchar | 门店编码(关联 dim_shop.tr_shop_code) |
| sku | varchar | SKU 编码(关联 dim_product) |
| skc | varchar | SKC 编码(款色) |
| color_code | varchar | 颜色编码 |
| color_name | varchar | 颜色名称 |
| spec_code | varchar | 规格编码 |
| spec_name | varchar | 规格名称 |
| style_code | varchar | 款式编码 |
| style_name | varchar | 款式名称 |
| stock_quantity | int | 库存数量 |
| stock_cost | decimal(20,4) | 库存成本 |
| stock_market_value | decimal(20,4) | 库存市值(吊牌价口径) |
| stock_quantity_onroad | int | 在途库存数量 |
| stock_cost_onroad | decimal(20,4) | 在途库存成本 |
| stock_market_value_onroad | decimal(38,4) | 在途库存市值 |
| stock_quantity_onord | int | 在单库存数量 |
| stock_cost_onord | decimal(20,4) | 在单库存成本 |
| stock_market_value_onord | decimal(29,4) | 在单库存市值 |
| stock_date | date | 库存日期 |
| snapshot_date | date | 快照日期(用于时间筛选) |
| discount_rate | decimal(38,9) | 折扣率 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| store_code | varchar | 门店编码(关联 dim_shop.tr_shop_code) |
| sku | varchar | SKU 编码(关联 dim_product) |
| skc | varchar | SKC 编码(款色) |
| zprod_code | varchar | 商品编码 |
| brand_code | varchar | 品牌编码 |
| brand_name | varchar | 品牌名称 |
| launch_date | date | 上市日期 |
| prod_year | varchar | 商品年份 |
| prod_season | varchar | 商品季节 |
| dt | varchar | 数据日期 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| sku | varchar | SKU 编码(主键) |
| skc | varchar | SKC 编码(款色) |
| product_sc_type | varchar | 商品小类 |
| product_sc_type_code | varchar | 商品小类编码 |
| mid_category | varchar | 中类 |
| mid_category_code | varchar | 中类编码 |
| little_category | varchar | 小类 |
| little_category_code | varchar | 小类编码 |
| style_code | varchar | 款式编码 |
| style_name | varchar | 款式名称 |
| age_group | varchar | 年龄段 |
| age_group_code | varchar | 年龄段编码 |
| brand_id | int | 品牌 ID |
| color_code | varchar | 颜色编码 |
| color_name | varchar | 颜色名称 |
| come_up_batch | varchar | 上市批次 |
| gender | varchar | 性别 |
| gender_code | varchar | 性别编码 |
| price_level | varchar | 价格带 |
| product_brand_code | varchar | 商品品牌编码 |
| product_brand_name | varchar(255) | 商品品牌名称 |
| product_hierarchy | varchar | 商品层级 |
| product_position_code | varchar | 商品定位编码 |
| real_market_date | varchar | 实际上市日期 |
| product_year | smallint | 商品年份 |
| product_season | varchar | 商品季节 |
| season | varchar | 季节 |
| season_code | varchar | 季节编码 |
| coefficient | varchar | 系数 |
| sub_brand_id | varchar(255) | 子品牌 ID |
| tag_price | decimal(20,4) | 吊牌价 |
| new_arrival_price | decimal(38,9) | 新品价 |
| dt | date | 数据日期 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| tr_shop_code | varchar | 门店编码-数仓 Key(主键,用于与事实表 JOIN) |
| shop_code | varchar | 门店编码(业务编码) |
| shop_id | bigint | 门店 ID |
| shop_brand_id | bigint | 门店品牌 ID |
| shop_leader | varchar | 店长姓名 |
| shop_leader_code | varchar | 店长编码 |
| shop_status | varchar | 门店状态 |
| shop_status_code | varchar | 门店状态编码 |
| province | varchar | 省份 |
| province_id | bigint | 省份 ID |
| city | varchar | 城市 |
| city_id | bigint | 城市 ID |
| city_grade | varchar | 城市等级 |
| city_grade_id | bigint | 城市等级 ID |
| district | varchar | 区县 |
| address | varchar | 地址 |
| shop_name | varchar | 门店名称 |
| business_area | int | 营业面积 |
| business_district_level | varchar | 商圈等级 |
| business_district_level_code | varchar | 商圈等级编码 |
| first_channel | varchar | 一级渠道 |
| first_channel_id | bigint | 一级渠道 ID |
| first_channel_code | varchar | 一级渠道编码 |
| second_channel | varchar | 二级渠道 |
| second_channel_id | bigint | 二级渠道 ID |
| second_channel_code | varchar | 二级渠道编码 |
| channel_type_id | bigint | 渠道类型 ID |
| opening_date | varchar | 开店日期 |
| closing_date | varchar | 闭店日期 |
| dt | date | 数据日期 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| dt | date | 数据日期 |
| vip_id | bigint | 会员 ID(主键) |
| vip_code | varchar | 会员编码 |
| vip_name | varchar | 会员姓名 |
| vip_level | varchar | 会员等级 |
| vip_level_code | varchar | 会员等级编码 |
| vip_status | varchar | 会员状态 |
| vip_status_code | varchar | 会员状态编码 |
| vip_reg_date | varchar | 注册日期 |
| vip_last_login_date | varchar | 最后登录日期 |
-- 订单表关联维度表
fact_orders.sku = dim_product.sku
fact_orders.shop_code = dim_shop.tr_shop_code
fact_orders.vip_id = dim_vip.vip_id
-- 库存表关联维度表
fact_inventory.sku = dim_product.sku
fact_inventory.shop_code = dim_shop.tr_shop_code
-- 商品上市表关联维度表
fact_product_launch.sku = dim_product.sku
fact_product_launch.store_code = dim_shop.tr_shop_code
⚠️ JOIN 关键注意事项:
tr_shop_code(数仓 Key),不是 shop_code(业务编码)。事实表中的 shop_code / store_code 对应的是 dim_shop.tr_shop_codevip_id(bigint),与 fact_orders 的 vip_id 关联sku,三张事实表都通过 sku 字段关联dt(date 类型)或 snapshot_date 字段SELECT
SUM(retail_amount) AS total_sales
FROM fact_orders
WHERE dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND dt < DATE_FORMAT(CURDATE(), '%Y-%m-01');
WITH current_month AS (
SELECT
ds.first_channel,
SUM(fo.retail_amount) AS sales
FROM fact_orders fo
JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY ds.first_channel
),
previous_month AS (
SELECT
ds.first_channel,
SUM(fo.retail_amount) AS sales
FROM fact_orders fo
JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 2 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
GROUP BY ds.first_channel
)
SELECT
c.first_channel,
c.sales AS current_sales,
p.sales AS previous_sales,
(c.sales - p.sales) / NULLIF(p.sales, 0) AS mom_growth
FROM current_month c
LEFT JOIN previous_month p ON c.first_channel = p.first_channel
ORDER BY c.sales DESC;
SELECT
dp.product_brand_name,
SUM(fo.retail_amount) AS total_sales,
COUNT(DISTINCT fo.order_number) AS order_count,
SUM(fo.retail_amount) / NULLIF(COUNT(DISTINCT fo.vip_id), 0) AS aov
FROM fact_orders fo
JOIN dim_product dp ON fo.sku = dp.sku
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY dp.product_brand_name
ORDER BY total_sales DESC
LIMIT 10;
SELECT
ds.first_channel,
SUM(fo.retail_amount) AS channel_sales,
SUM(fo.retail_amount) / SUM(SUM(fo.retail_amount)) OVER () AS sales_proportion
FROM fact_orders fo
JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY ds.first_channel
ORDER BY channel_sales DESC;
SELECT
fo.dt,
SUM(fo.retail_amount) AS daily_sales
FROM fact_orders fo
WHERE fo.dt >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
AND fo.dt < CURDATE()
GROUP BY fo.dt
ORDER BY fo.dt;
⚠️ StarRocks 兼容性提示:StarRocks 兼容 MySQL 语法,但注意:
- 使用
DATE_FORMAT、DATE_SUB、CURDATE()等标准函数- 窗口函数(
OVER())完全支持NULLIF用于避免除零错误
在写任何 SQL 之前,先把用户的问题拆解为四个维度:
| 维度 | 问自己 | 示例 |
|---|---|---|
| 看什么(指标) | 用户要看的业务量是什么? | "销售额""客单价""库存量" |
| 怎么看(分析方式) | 直接看值?还是要对比/占比/排名/趋势? | "同比增长率""占比""排名前5""月趋势" |
| 看谁的(维度 & 筛选) | 按什么维度拆分?筛选哪些范围? | "各品牌""某渠道""某地区" |
| 看哪段时间(时间) | 时间范围是什么? | "上月""近7天""某月 vs 另一月" |
本数据库没有统一的指标定义层。你需要根据 §1 中的表结构和字段说明,自行推断用户业务术语对应的 SQL 写法。
⚠️ 这是 Text-to-SQL 最容易出错的环节。以下是常见的歧义陷阱,务必留意:
陷阱 A — 金额口径歧义:fact_orders 中有多个金额字段(retail_amount、net_amount、retail_market_value 等),用户说"销售额"时到底指哪个?不同口径的差异可能很大。当无法确定时,应在查询解读中说明你采用的口径。
陷阱 B — 复合指标的计算方式:像"客单价""连带率""件单价""折扣率""退货率"这类复合指标,需要你根据业务含义自行组合字段计算。同一个术语可能有多种合理的计算方式(例如"客单价"的分母是人数还是单数?),不同算法的结果可能相差数十倍。
陷阱 C — 跨表指标:有些指标可能涉及多张表的数据(如库存相关指标在 fact_inventory,销售相关在 fact_orders)。注意判断指标来自哪张表,不要在错误的表上查询。
陷阱 D — 数据库能力边界:本数据库是零售订单、库存和商品数据,不包含所有业务数据。如果用户询问的指标在现有表中找不到对应字段,应如实告知,不要强行拼凑。
规则 A — "总和" vs "分别"
| 用户说 | 含义 | GROUP BY |
|---|---|---|
| "A 和 B 的某指标分别是多少" | 按 A、B 分组展示 | 保留分组 |
| "A 和 B 的某指标总和" | A+B 合并为一个数字 | 不分组 |
| "A 和 B 的某指标"(无修饰) | 默认"分别" | 保留分组 |
规则 B — "占比" 的两种含义
| 用户说 | 含义 | SQL 实现 |
|---|---|---|
| "某指标占比"(如"销售额占比") | 值占比 | SUM(x) / SUM(SUM(x)) OVER() |
| "某实体占比"(如"款色占比") | 数量占比 | COUNT(DISTINCT entity) / (SELECT COUNT(DISTINCT entity) FROM ...) |
规则 C — "同比" vs "环比"
| 用户说 | 含义 | SQL 实现 |
|---|---|---|
| "同比"(无限定) | 年同比 (YoY) | 与去年同期对比 |
| "月同比" | 与上月同期对比 | 上个月的同日/同周 |
| "环比" | 与上一个周期对比 | 根据粒度:日→前一天,月→前一月 |
规则 D — 简单优先
如果用户问题可以用简单 SQL 回答,就不要写复杂的 CTE 和窗口函数。
tr_shop_code,不要用 shop_code-- 示例:按一级渠道查销售额
SELECT ds.first_channel, SUM(fo.retail_amount) AS retail_amt
FROM fact_orders fo
LEFT JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= '2026-02-01' AND fo.dt < '2026-03-01'
GROUP BY ds.first_channel
ORDER BY retail_amt DESC;
-- 示例:按品牌查销售额
SELECT dp.product_brand_name, SUM(fo.retail_amount) AS retail_amt
FROM fact_orders fo
LEFT JOIN dim_product dp ON fo.sku = dp.sku
WHERE fo.dt >= '2026-02-01' AND fo.dt < '2026-03-01'
GROUP BY dp.product_brand_name
ORDER BY retail_amt DESC;
-- 示例:按会员等级查购买人数
SELECT dv.vip_level, COUNT(DISTINCT fo.vip_id) AS vip_count
FROM fact_orders fo
LEFT JOIN dim_vip dv ON fo.vip_id = dv.vip_id
WHERE fo.dt >= '2026-02-01' AND fo.dt < '2026-03-01'
GROUP BY dv.vip_level
ORDER BY vip_count DESC;
时间字段说明:
dt(date 类型)做时间筛选snapshot_date(date 类型)做时间筛选launch_date(date 类型)做时间筛选相对时间转换:
| 用户说 | WHERE 条件 |
|---|---|
| 上月 | dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01') AND dt < DATE_FORMAT(CURDATE(), '%Y-%m-01') |
| 本月 | dt >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND dt < DATE_ADD(DATE_FORMAT(CURDATE(), '%Y-%m-01'), INTERVAL 1 MONTH) |
| 昨天 | dt = DATE_SUB(CURDATE(), INTERVAL 1 DAY) |
| 近7天 | dt >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND dt < CURDATE() |
| 近30天 | dt >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND dt < CURDATE() |
| 本年 | dt >= DATE_FORMAT(CURDATE(), '%Y-01-01') |
| 去年 | dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 YEAR), '%Y-01-01') AND dt < DATE_FORMAT(CURDATE(), '%Y-01-01') |
同环比需要用 CTE 或子查询分别获取两个时期的数据再对比:
-- 月环比示例:上月 vs 上上月整体销售额
WITH current_month AS (
SELECT SUM(retail_amount) AS current_val
FROM fact_orders
WHERE dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND dt < DATE_FORMAT(CURDATE(), '%Y-%m-01')
),
previous_month AS (
SELECT SUM(retail_amount) AS previous_val
FROM fact_orders
WHERE dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 2 MONTH), '%Y-%m-01')
AND dt < DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
)
SELECT
cm.current_val,
pm.previous_val,
cm.current_val - pm.previous_val AS change_value,
(cm.current_val - pm.previous_val) / NULLIF(pm.previous_val, 0) AS growth_rate
FROM current_month cm, previous_month pm;
按维度拆分的同环比更加复杂,需要用 FULL JOIN:
-- 各渠道月环比
WITH current_month AS (
SELECT ds.first_channel, SUM(fo.retail_amount) AS current_val
FROM fact_orders fo
LEFT JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY ds.first_channel
),
previous_month AS (
SELECT ds.first_channel, SUM(fo.retail_amount) AS previous_val
FROM fact_orders fo
LEFT JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 2 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
GROUP BY ds.first_channel
)
SELECT
COALESCE(cm.first_channel, pm.first_channel) AS `一级渠道`,
cm.current_val AS `本月销售金额`,
pm.previous_val AS `上月销售金额`,
(cm.current_val - pm.previous_val) / NULLIF(pm.previous_val, 0) AS `月环比增长率`
FROM current_month cm
FULL JOIN previous_month pm ON cm.first_channel = pm.first_channel
ORDER BY `月环比增长率` DESC;
-- 各渠道销售额占比
SELECT
ds.first_channel,
SUM(fo.retail_amount) AS retail_amt,
SUM(fo.retail_amount) / SUM(SUM(fo.retail_amount)) OVER() AS proportion
FROM fact_orders fo
LEFT JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY ds.first_channel
ORDER BY retail_amt DESC;
-- 品牌销售额 TOP 5
SELECT
dp.product_brand_name,
SUM(fo.retail_amount) AS retail_amt,
RANK() OVER(ORDER BY SUM(fo.retail_amount) DESC) AS rk
FROM fact_orders fo
LEFT JOIN dim_product dp ON fo.sku = dp.sku
WHERE fo.dt >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND fo.dt < DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY dp.product_brand_name
ORDER BY retail_amt DESC
LIMIT 5;
每次执行查询时,必须按以下三段式结构向用户展示信息,缺一不可,顺序不可颠倒:
用一段自然语言向用户解释"查了什么、怎么查的",让非技术用户也能理解查询含义。
写作要求:
展示完整的、格式化的 SQL 语句:
SELECT ...
FROM ...
WHERE ...
GROUP BY ...
ORDER BY ...;
以表格形式展示查询返回的数据结果。列名使用中文展示名。
示例 A — 简单查询:用户问"上月的销售额是多少?"
📊 查询解读
帮你查询了上月(2026年2月)的零售金额(retail_amount 求和),查的是整体总量,没有按维度拆分。
📋 SQL 查询语句:
SELECT SUM(retail_amount) AS `零售金额`
FROM fact_orders
WHERE dt >= '2026-02-01' AND dt < '2026-03-01';
📈 查询结果: 上月(2026年2月)的零售金额为 12,345,678 元。
示例 B — 带维度和环比的复杂查询:用户问"上月各渠道的销售额及月环比增长率,增速前5名是哪些?"
📊 查询解读
帮你查询了上月(2026年2月)各一级渠道的零售金额(retail_amount 求和),并用 CTE 分别计算了本月和上月的数据后做对比,得出月环比增长率。结果按环比增长率从高到低排列,取前 5 名。
📋 SQL 查询语句:
WITH current_month AS (
SELECT ds.first_channel, SUM(fo.retail_amount) AS current_val
FROM fact_orders fo
LEFT JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= '2026-02-01' AND fo.dt < '2026-03-01'
GROUP BY ds.first_channel
),
previous_month AS (
SELECT ds.first_channel, SUM(fo.retail_amount) AS previous_val
FROM fact_orders fo
LEFT JOIN dim_shop ds ON fo.shop_code = ds.tr_shop_code
WHERE fo.dt >= '2026-01-01' AND fo.dt < '2026-02-01'
GROUP BY ds.first_channel
)
SELECT
COALESCE(cm.first_channel, pm.first_channel) AS `一级渠道`,
cm.current_val AS `本月零售金额`,
pm.previous_val AS `上月零售金额`,
(cm.current_val - pm.previous_val) / NULLIF(pm.previous_val, 0) AS `月环比增长率`
FROM current_month cm
FULL JOIN previous_month pm ON cm.first_channel = pm.first_channel
ORDER BY `月环比增长率` DESC
LIMIT 5;
📈 查询结果:
| 一级渠道 | 本月零售金额 | 上月零售金额 | 月环比增长率 |
|---|---|---|---|
| 电商 | 5,234,567 | 4,426,012 | +18.3% |
| 直营 | 3,456,789 | 3,083,754 | +12.1% |
| ... | ... | ... | ... |
按 §2 将用户问题拆解为"看什么 / 怎么看 / 看谁的 / 看哪段时间"四维度。检查 §2.2 的消歧规则。
生成 SQL 后,逐项检查:
通过 §0 的 Gateway JDBC API 执行 SQL,按 §4 的三段式格式展示结果。
执行注意事项:
Table not allowed 错误,说明查询了白名单以外的表,检查 §1.1 的可用表列表WHERE sku = 'SKU1000211')❌ 模式 1:金额字段选错 retail_amount(零售金额)和 net_amount(净销售金额)差异可能很大。必须确认用户意图。
❌ 模式 2:客单价算法歧义
SUM(retail_amount) / COUNT(DISTINCT vip_id) = 人均消费金额SUM(retail_amount) / COUNT(DISTINCT order_number) = 单均金额❌ 模式 3:dim_shop JOIN 键用错
事实表的 shop_code 对应 dim_shop 的 tr_shop_code(数仓 Key),不是 dim_shop 的 shop_code(业务编码)。这是最容易犯的错误。
❌ 模式 4:同环比时间范围写错 手动计算两个时期的时间范围容易出错,特别是跨年、跨月场景。
❌ 模式 5:维度值不确定 不知道维度表中具体有哪些值(如一级渠道到底有哪些),应先查询确认:
SELECT DISTINCT first_channel FROM dim_shop;
SELECT DISTINCT product_brand_name FROM dim_product;
SELECT DISTINCT vip_level FROM dim_vip;
❌ 模式 6:缺少行业术语理解 数据库 schema 不包含业务语义。例如"动销率""售罄率""库销比"等行业指标,纯靠 schema 无法推断其计算公式。
❌ 模式 7:GROUP BY 缺失列 SELECT 中有非聚合列但 GROUP BY 中遗漏,在严格模式下会报错。
❌ 模式 8:除零错误 环比增长率等计算中,分母可能为 0 或 NULL,必须用 NULLIF 处理。
❌ 模式 9:库存表时间字段用错
fact_inventory 的时间筛选用 snapshot_date,不是 dt(该表无 dt 字段)。fact_orders 的时间筛选用 dt。
本 Skill 直接基于数据库 schema 生成 SQL,存在以下固有局限: