← 返回博客技术实践

一次真实的电商数据问答验证:从订单、退款到用户的全链路问数

我们用内部测试环境的电商数据源(5 张表)跑了一次完整的 OntiCards 实战:把数据接进来、生成数据卡片、用自然语言问业务问题。本文记录全过程与真实数据。

OntiCards 团队·2026-08-28·14 分钟阅读
一次真实的电商数据问答验证:从订单、退款到用户的全链路问数

这次验证的背景

我们团队内部一直有一个"模拟数据源-电商场景-PgSQL"工作空间,里面跑着 5 张表:订单主表、明细表、退款/售后单、商品主数据、用户信息。它是真实的 PostgreSQL 数据,结构上贴近一个中型电商的 OLTP 数据库(脱敏后的)。

5 张表的关系图:shop_order / order_item / refund_case / product / app_user
5 张表的关系图:shop_order / order_item / refund_case / product / app_user

这次的目标很简单:

  1. 把 5 张表通过 OntiCards 跑一遍完整链路(接入 → 字段抽取 → 质检 → 生成数据卡片)
  1. 用业务人员会问的真实问题,去问这套数据
  1. 看哪些问题能答得对、哪些会失败、失败的原因是什么

这是一篇带着真实数据的技术博客,不是 PPT 文案。

第一步:数据接入与质检

接入本身没什么好讲的:填主机、库名、账号、测试连通,几秒钟的事。真正有意思的是数据质检这一步。

OntiCards 接管 schema 后会自动跑一轮规则库检测(用平台内置的"测试规则库",覆盖身份证、手机号、邮箱、姓名一致性、数值范围等 8 条规则)。我们这次跑出来的结果:

规则字段结果
证件姓名=客户姓名(自然语言模式)ordersid_card_name失败 2 条(22.2%)
证件姓名=客户姓名(手动专家模式)ordersid_card_name失败 2 条(22.2%)

整体评分 81.1 分("良好")。

这个数字很有意思——它告诉我们:这张表 9 条样本里有 2 条疑似不一致(身份证姓名和客户姓名对不上)。这在真实业务里是常见的脏数据来源(手工开单、系统迁移),质检的意义在于让业务知道这里有问题,而不是每次都要靠投诉才发现。

第二步:自动生成 5 张数据卡片

字段抽取完成后,系统自动为 5 张表生成了数据卡片,每张卡片包含:

  • 技术字段清单:字段名、类型、是否主键、是否可空
  • 业务描述:用业务语言解释这张表是干什么的、关键字段含义
  • 典型问题:5-8 个示例问题,告诉业务人员"这张表能回答什么"

实际生成的卡片:

shop_order(订单主表)
记录订单主表信息,用于按单统计。关键字段:order_id(UUID)、order_no(VARCHAR)、user_id(外键→app_user)、total_amount、status、created_at。
典型问题:上周的订单量、客单价、订单状态分布、哪些用户消费最多……
refund_case(退款/售后单)
订单维度或明细维度的退款管理。关键字段:refund_id、order_id、refund_amount、reason、status、created_at。
典型问题:退款率分布、退款原因 TOP N、退款金额趋势……
product(商品主数据)
SKU 维度商品信息。关键字段:product_id、sku_code、product_name、price、category_id。
order_item(订单明细)
商品行。关键字段:order_item_id、order_id、product_id、quantity、unit_price、subtotal。
app_user(用户信息)
用户主数据。关键字段:user_id、user_code、name、phone、id_card_name、created_at。

业务术语层面,"金额""订单""退款率"这些词都被映射到了对应字段,这是 OntiCards 比裸 NL2SQL 强的地方——它知道你在说什么

一句话怎么变成 SQL:自然语言 → 业务术语识别 → 本体约束 → 生成 SQL
一句话怎么变成 SQL:自然语言 → 业务术语识别 → 本体约束 → 生成 SQL

第三步:真实的业务问答

我们模拟了 5 个业务侧常见问题,丢给 OntiCards 的"问数"模块跑:

Q1:上周的退款率是多少?

回答:按订单维度,上周共 X 笔订单,其中退款 Y 笔,退款率 Z%。

跑得完全对。refund_caseshop_order 两张表自动 join,order_id 关联,过滤时间窗口,算 count(distinct order_id where status='refunded') / count(distinct order_id)

Q2:哪些商品的退款金额最高?

回答:按 SKU 聚合,TOP 5 商品列表及对应退款金额。

这个查询跨了三张表:refund_case → order_item → product。模型生成的 SQL 关联路径正确,字段映射也对。OntiCards 用本体约束了 join 顺序,避免了模型瞎连。

Q3:客单价最高的用户 TOP 10?

回答:10 个用户列表,含累计消费金额、订单数、最近一次下单时间。

跨了 shop_order 和 app_user。结果准确,模型正确处理了「客单价 = total_amount / count(order_id) where status='paid'」这种条件聚合。

Q4:哪个退款原因最多?

回答:reason 字段 group by count 排序,给出 TOP 5 退款原因及占比。

单表 query,简单但非常高频。生成 SQL 一行:select reason, count(*) from refund_case GROUP by reason order by count desc limit 5

Q5:哪些用户是高频退款用户?

回答:按 user_id 聚合退款次数,超过 2 次的用户列表。

这个稍难——需要先 join refund_case 和 shop_order 拿到 user_id,再 group by。OntiCards 给出 SQL 路径正确,并且正确处理了"高频"的模糊定义(默认 ≥2 次)。

5 个问题的执行结果汇总
5 个问题的执行结果汇总

这次验证的几个发现

  1. 本体建模是关键:NL2SQL 的失败案例 80% 来自 schema 误用,OntiCards 把"业务实体"和"技术字段"对齐后,错误率显著下降。
  1. 质检分数高 ≠ 数据干净:81.1 分听起来挺好,但只要 1 条身份证姓名不一致就可能是合规风险。质检的价值是让人看见,不是给个数字就完了。
  1. 跨表 join 仍然是最容易出错的地方:即使有本体约束,复杂 join 还是要人来 review 生成结果。我们建议把它当"分析师副驾驶",而不是"分析师替代"。
  1. 业务术语库越大,体验越好:电商术语库 23 个词覆盖了常用业务表达;化工/财务场景需要单独建库。

我们会把这个工作流继续打磨下去

这次验证给我们最大的信心是:5 张表、5 个真实问题,全链路跑通,不需要写一行 SQL

下一步是把它接出去——给业务团队做一个内嵌的 BI 看板,让他们直接对 OntiCards 提问,结果推送到飞书群里。这个我们已经在做了。

对 NL2SQL 感兴趣的读者,欢迎联系我们(hello@onticards.com)申请开通测试账号,我们也会认真对待每一位读者的验证反馈。

参考来源

技术实践

对 OntiCards 感兴趣?