把数据库交给 Codex 后,生成一条能运行的 SQL 并不难。真正麻烦的是:销售额该取哪个字段,时间按下单还是开票,结果又该拿什么核对。
上一篇《从0开始,一句话启动AI DataAgent》用内置 SQLite 样例跑通了安装和 MCP 调用。这一次,我把 Microsoft Wide World Importers 恢复到本机 SQL Server,让 Codex 从销售分析开始建立语义层。
先把这段话交给 Codex
这里默认已经按照上一篇启动了本地 DataAgent,并把 Wide World Importers 恢复到本机 SQL Server,准备好只读连接。
第一次开始时,不需要先查清所有表名、字段名和指标口径。我给 Codex 的任务只有这些:
请使用 foggy-ai-analysis Skill,连接我本机 Docker SQL Server 中的 WideWorldImportersDW 只读 datasource。
第一版先从销售分析开始,不接全库。我希望能查询销售趋势、客户排名、商品利润、库存情况,以及订单金额和开票销售额的差异。
请先检查相关表结构和数据范围,列出需要我确认的问题。确认后再建立第一版语义模型,并准备一组可以核对结果的题库。
这段任务只提供数据库、分析方向和希望回答的问题。后面的表结构检查、候选字段定位、模型生成和题库准备由 Codex 推进;用户或业务人员负责确认销售额、时间范围等关键口径,并验收最终结果。
第一版只建立了三个可查询模型(QueryModel),准备了 26 条题库,其中 21 条执行通过,5 条用于记录需要澄清或停止的边界。下面是具体过程。
不接全库,先选销售分析
Wide World Importers 包含 OLTP 库 WideWorldImporters 和分析库 WideWorldImportersDW,覆盖客户、订单、发票、商品、库存和日期等常见业务对象。相比临时准备几张 demo 表,它更接近实际业务系统。
但第一版没有接入所有表,只选择了 WideWorldImportersDW 的销售分析域。仅这个范围,就有几组容易混淆的口径:
- 订单金额和开票销售额不是一回事。
- 含税金额、不含税金额和利润不能混用。
- 下单日期和发票日期会得到不同的统计结果。
- 客户榜单里要不要包含
Unknown会直接影响 TopN 结果。 - 数据库里有日期维表到 2016 年底,但销售事实数据只到
2016-05-31。
如果不事先约定,同一个问题可以生成多种都能执行、但含义不同的 SQL。因此,我没有让 Codex 从全库自由选择,而是先用问题限定范围。
建模之前,先看清数据范围
我把官方 .bak 恢复到本机 Docker SQL Server,并为 DataAgent 创建了只读账号。读取查询可以正常运行,建表操作会被 SQL Server 拒绝。生产环境还需要补充审计和数据权限,本次示例不展开。
数据库恢复后,我没有马上生成模型,而是先检查表规模和数据日期范围:
| 对象 | 行数 | 日期范围 |
|---|---|---|
Fact.Sale | 228265 | 2013-01-01 到 2016-05-31 |
Fact.Order | 231412 | 2013-01-01 到 2016-05-31 |
Dimension.Date | 1461 | 2013-01-01 到 2016-12-31 |
Fact.Stock Holding | 227 | 当前库存快照 |
这里很快发现了第一个问题:日期维表覆盖了完整的 2016 年,但销售和订单事实只到 5 月 31 日。
也就是说,查询即使能按日期维表筛选全年,也不能把结果称为“2016 全年销售额”。后面的模型和题库统一使用“2016 年样例事实期”,或者直接标明 2016-01-01 到 2016-05-31。
先列问题,再定模型
接着,我没有继续浏览更多表,而是先整理了一组需要验收的问题:
- 2016 年样例事实期销售额、利润、数量是多少?
- 2016 年样例事实期销售额最高的客户是谁?
- 按月份看销售额趋势是什么?
- 哪些商品利润最高?
- 当前哪些库存低于补货线?
- 订单金额和开票销售额有什么差异?
- 2016 年 1-5 月相比 2015 年同期,哪些客户下降最多?
- 如果用户问“2016 全年销售额”,系统应该怎么澄清?
这些问题最终对应三个 QueryModel。没有被题库用到的业务域,暂时不接:
| QueryModel | 作用 |
|---|---|
wwi_sales_analysis | 默认销售分析模型,基于 Fact.Sale,使用发票日期和开票销售额 |
wwi_order_analysis | 订单金额口径对照,基于 Fact.Order,不替代销售额 |
wwi_inventory_signal | 当前库存、补货线、目标库存和低库存信号 |
默认销售模型 wwi_sales_analysis 明确了三个口径:
默认销售日期:Fact.Sale.[Invoice Date Key]
默认销售额:Fact.Sale.[Total Including Tax]
默认客户榜单:排除 Dimension.Customer.Customer = 'Unknown'
题库还保留了一条包含 Unknown 的对照查询。结果很直观:Unknown 会以 10224728.14 的销售额排在第一名。因此,默认客户榜单将它排除,用户明确要求时再纳入统计。
订单则单独建模。2016 年样例事实期内:
开票销售额来自 Fact.Sale
订单金额来自 Fact.Order
两者的结果也不同:开票销售额含税金额是 25971029.11,订单含税金额是 26848038.92。这也是订单和销售需要分开建模的直接证据。
26 条题库,既测答案也测边界
模型生成后,我把问题固化成题库,并运行模型校验、查询执行和业务断言。
题库共 26 条:21 条执行通过,5 条边界题按计划不执行,没有失败项。
这 5 条边界题记录第一版不执行或需要澄清的问题:
- 预测下个月销售额。
- 把下降客户写回数据库并标记高风险。
- 编造利润下降原因。
- 查询联系人或登录信息。
- 用户问“2016 全年销售额”时,需要澄清数据只到
2016-05-31。
这 5 条没有进入查询执行,因此不计入失败。这里记录的是项目边界,不能解释成系统已经执行并自动拒绝了这些请求。
其余 21 条覆盖汇总、TopN、趋势、商品利润、库存异常、同比下降,以及订单与销售口径对照。它们不复杂,但都能用手写 SQL 核对,适合第一版验收。
最后得到的不是一条 SQL
一条 SQL 能运行,不代表它回答的是用户心里的那个问题。日期是否完整、销售额采用哪个口径、订单和开票能否对比,都要在建模和验收阶段明确。
在这套流程中,语义层固定指标和维度的含义,题库检查结果与项目边界。Codex 可以检查字段、生成语义模型、执行验证和整理证据;是否通过验收,仍取决于结果能否复核、口径是否一致。
下一步
下一篇不再重复建模,而是直接使用这三个模型分析一个具体问题:近一个月哪些客户的销售量明显下降?我会拆解时间窗口、客户范围、销售口径和下降判定,看看从基础问数走向分析时还缺哪些约束。
参考资料和内部验证记录:
- Microsoft Wide World Importers 官方说明:
https://learn.microsoft.com/en-us/sql/samples/wide-world-importers-what-is - Microsoft SQL Server samples 仓库:
https://github.com/microsoft/sql-server-samples/tree/master/samples/databases/wide-world-importers - Fabric Data Agent example queries 方法参考:
https://learn.microsoft.com/en-us/fabric/data-science/data-agent-example-queries docs/wide-world-importers-dataagent-evidence/2026-07-07-wide-world-importers-sqlserver-restore.mddocs/wide-world-importers-dataagent-evidence/2026-07-07-wide-world-importers-dataagent-validation.mddocs/wide-world-importers-dataagent-evidence/2026-07-08-wide-world-importers-question-bank-expansion-research.md