AI 写出的查询能运行,结果也可能错得很整齐。订单表一行一笔订单,退款表一行一次退款;同一订单退过两次,直接连接后订单金额就会出现两遍。本篇用四笔虚构订单,在 DB Browser for SQLite 中复现这种重复,再让 AI 写出先汇总退款、后连接的查询。适合会看表格、刚开始接触 SQL 的运营和分析人员。
准备一个隔离的练习数据库
安装官方 DB Browser for SQLite桌面版,它是开源可视化 SQLite 工具,不需要数据库服务器或云账号。AI 文本工具的登录、额度和费用另按你的服务确认;本例不需要付费模型或 API。只使用本文数据,不连接业务库,不把客户记录交给 AI。金额全部用整数“分”,避免把浮点显示误差混进连接问题。
新建空数据库,命名 practice.db。若立即弹出建表对话框,可取消,在“执行 SQL(Execute SQL)”页运行下面的初始化脚本,再点击“写入更改(Write Changes)”。这段脚本只在新建练习库运行一次;它会创建两张表并写入假数据,绝不能粘到工作数据库中。
CREATE TABLE orders (
id TEXT PRIMARY KEY,
channel TEXT NOT NULL,
amount_cents INTEGER NOT NULL
);
CREATE TABLE refunds (
id TEXT PRIMARY KEY,
order_id TEXT NOT NULL,
amount_cents INTEGER NOT NULL
);
INSERT INTO orders VALUES
('A01','web',10000), ('A02','web',10000),
('A03','store',8000), ('A04','store',20000);
INSERT INTO refunds VALUES
('R01','A01',1000), ('R02','A01',500),
('R03','A04',20000);
第一步:把查询环境改成只读
保存并关闭练习库,在终端中按下面方式重新打开。项目 GitHub Wiki给出的 -R 参数是只读打开;这项能力在本练习里用来限制后续 AI 查询的影响,而不是仅靠提示词说“不要改数据”。
sqlitebrowser -R "practice.db"
命令里的 sqlitebrowser 要替换为你安装的程序实际可执行文件路径,数据库也可写完整路径。若提示找不到命令,先查安装位置或该程序的 --help,不要改用不认识的下载器。无法确认只读打开时,停在练习副本,不要拿正式数据继续。只读不是允许执行任意 SQL 的理由,仍应拒绝 AI 给出的写入语句、扩展加载和外部文件操作。
第二步:给 AI 表结构、样例和核对口径
请为 SQLite 写一条只读汇总查询,并解释每个中间结果一行代表什么。不要生成建表、更新、删除、附加数据库或加载扩展的语句。
目标:按 channel 统计订单数、订单金额、退款金额、净额;全部以整数“分”为单位,净额定义为本练习的订单金额减退款金额。不涉及会计收入确认。没有退款的订单也要保留;一个订单可以有多条退款。
orders(id TEXT PRIMARY KEY, channel TEXT, amount_cents INTEGER)
A01|web|10000
A02|web|10000
A03|store|8000
A04|store|20000
refunds(id TEXT PRIMARY KEY, order_id TEXT, amount_cents INTEGER)
R01|A01|1000
R02|A01|500
R03|A04|20000
请先说明直接连接明细再 SUM(orders.amount_cents) 会怎样重复计算。再用先按 order_id 汇总退款、后 LEFT JOIN 的方式给出一条 WITH ... SELECT;不要用 SUM(DISTINCT 订单金额) 掩盖重复。列名为 channel、order_count、gross_cents、refund_cents、net_cents;按 channel 排序。最后手算两行预期值,并给出验证 A02、A03 无退款仍被保留的方法。
这里故意让 A01 与 A02 的订单金额相同:用 SUM(DISTINCT amount_cents) 会把两个不同订单的同额金额合成一个,也不是正确修复。SQLite 的聚合函数文档说明 DISTINCT 去掉的是参数里的重复值,并不理解哪个业务订单应保留。
第三步:先看重复怎么发生
在新的执行 SQL 页中只放下面这条诊断查询,运行后应看到 A01 两行,分别对应 R01、R02;A02、A03 的退款编号为空。LEFT JOIN 保留未匹配订单,符合本练习要求。相关连接规则见SQLite SELECT 文档。
SELECT o.id, o.amount_cents, r.id AS refund_id
FROM orders AS o
LEFT JOIN refunds AS r ON r.order_id=o.id
ORDER BY o.id, r.id;
诊断结果共五行,却只有四个订单。若此时按渠道累加订单金额,web 会被算成 30000 分,而正确值是 20000 分。看到这个差异后,再核对 AI 的修正语句,不要只看它有没有语法错误。
第四步:运行预汇总后的查询
下面是可用来对照的完整查询。退款先在 CTE 中收敛到每个 order_id 一行,再与订单连接;无退款时用 COALESCE 视为 0。WITH 文档和COALESCE 文档可用于核对这两个写法。注意 WITH 也能接写入语句,不能只看开头就判断安全。
WITH refund_by_order AS (
SELECT order_id, SUM(amount_cents) AS refund_cents
FROM refunds
GROUP BY order_id
)
SELECT o.channel,
COUNT(*) AS order_count,
SUM(o.amount_cents) AS gross_cents,
SUM(COALESCE(r.refund_cents,0)) AS refund_cents,
SUM(o.amount_cents-COALESCE(r.refund_cents,0)) AS net_cents
FROM orders AS o
LEFT JOIN refund_by_order AS r ON r.order_id=o.id
GROUP BY o.channel
ORDER BY o.channel;
将 AI 的回答和这份参考逐项比较,再把通过检查的一条查询放到新的 SQL 页执行。不要连同建表脚本一起运行。执行入口可用工具栏,也可按官方快捷键说明中的 Ctrl+Return。结果应恰好有以下两行,列中的金额单位仍是分,不要为了显示元而提前四舍五入:
| channel | 订单数 | 订单金额/分 | 退款/分 | 净额/分 |
|---|---|---|---|---|
| store | 2 | 28000 | 20000 | 8000 |
| web | 2 | 20000 | 1500 | 18500 |
第五步:用三道小检查验收
- 行数:原订单是 4 行,预汇总退款只有 A01、A04 两行;连接后必须恢复为 4 行,而不是直接连接的 5 行。
- 逐笔:A01 净额 8500,A02 为 10000,A03 为 8000,A04 为 0。A02、A03 没有退款也不能消失。
- 总额:两渠道合计订单金额 48000、退款 21500、净额 26500 分;不要只核对某一渠道。
逐笔核对可直接运行下面这条完整查询,四笔订单都应保留。检查通过后保存正式渠道汇总查询为 summary.sql,并另外记录预期结果;保存 SQL 文件不等于保存数据库更改。
WITH refund_by_order AS (
SELECT order_id, SUM(amount_cents) AS refund_cents
FROM refunds
GROUP BY order_id
)
SELECT o.id,
o.amount_cents AS gross_cents,
COALESCE(r.refund_cents,0) AS refund_cents,
o.amount_cents-COALESCE(r.refund_cents,0) AS net_cents
FROM orders AS o
LEFT JOIN refund_by_order AS r ON r.order_id=o.id
ORDER BY o.id;
若金额偏大,先查连接后的订单 id 是否重复;若无退款订单消失,查是否用了 INNER JOIN 或在 WHERE 中要求退款字段非空;若仍为 NULL,查是否漏了 COALESCE;若表已存在,不要重复跑初始化,用新的空库重新练习。更多订单表还可能有币种、取消状态、退款时间等条件,不能把本题口径原封不动套进实际经营报表。把一条看起来合理的查询交付前,先写清每张表一行代表什么、关联键是否唯一、缺少匹配时保留哪一边;这些约定比让 AI 多写几句解释更能帮助别人复核。
本文已用本地 Python sqlite3 执行这些合成样例并核对数值;没有声称做过 DB Browser 图形界面实测。官方资料核对日期为 2026 年 10 月 30 日。读完项目 README 后如何决定试用边界,可参考《看见GitHub AI项目先别安装:把README读成试用决策》;这里具体落实为隔离样例、只读打开和结果复算。