← 洞见最新技术展示

43 张 ERP 表接进 AI 的三道工序

泛友数字2026/10/27 11:4238 阅读
43 张 ERP 表接进 AI 的三道工序

Error 1054 (42S22): Unknown column 'ckSl' in 'field list'。这是把 ERP 出库表直接接给模型之后最常见的一条报错。SQL 语法没错,模型也没算错,它只是把「出库数量」翻译成了它以为的那个列名。

错的往往不是 SQL,是表。Spider 2.0(arXiv:2411.07763,ICLR 2025)收了632个企业级真实 text-to-SQL 工作流任务,库里的表常常超过1,000列,平均812列。基于 o1-preview 的代码代理框架只解出21.3%,同一框架在 Spider 1.0 上是91.2%,在 BIRD 上是73.0%。论文列出的第一条障碍是「从超大库里准确地链接 schema」。

下面这本手册按四类反复出现的故障排:症状、定位、改法、验证。四类都过一遍,才算接上;跳过任何一类,模型挑错表的概率都高到没法用。

症状:报错1054,或者模型挑了一张名字最像、语义不对的表。字段名是给程序员看的拼音缩写,出库数量叫CKSL,验收单号叫YSDH,模型只能猜。同一个订单号在订单表和发货表里也不是一回事。

定位:取十条真实提问,把模型生成的 SQL 里的列名逐条对照原表。错误集中在一处,就是字典缺,不是模型弱。这一步不用改任何代码,只需要一张对照表。

改法:建字段字典,每个字段一行,至少七列——物理名、中文名、业务含义、粒度、取值口径、空值语义、能否聚合。格式用 YAML 或 JSON,因为它既要当工具描述喂给模型,也要当回归测试的比对基准。命名同步统一,视图层全小写、下划线分词。字典里最难写的是「业务含义」:「产量」在完工入库口径和报工口径下差一个返修环节,在制品算不算各家说法不同,这类分歧不要合并,拆成两行,各写口径和负责人。

验证:挑三十个业务同事真会问的问题,人工写出正确答案并连 SQL 存好,跑一遍记下准确数。之后改字典、改视图、换模型都跑同一套,看这个数掉没掉。字段中文名覆盖不到八成,先补字典,别急着上线;字典比模型便宜,也比模型可靠。

症状:排序错,「9」排在「10」后面;金额汇总出现0.30000000000000004这类尾巴;月报和跨月报表对不上同一个数。

定位:查 information_schema,看金额、数量列的 DATA_TYPE。varchar 的比较按字符串走,FLOAT 存钱必然带误差,这两类都在原表里,改模型没有用。

改法:在视图层 CAST 成 DECIMAL。MySQL 8.4 手册 13.1《Numeric Data Types》把 DECIMAL、NUMERIC 归为精确数值类型,FLOAT、DOUBLE 归为近似类型,两者的语义差别不在精度位数,在能不能用于等值比较(dev.mysql.com/doc/refman/8.4)。日期统一成 DATE 或带时区的 DATETIME。时间粒度单独处理:建一张日期维表,事实表带两个时间外键——业务发生时间和记账期间,跨月报表用哪一个,在字典里写死。

验证:取同一段区间,视图汇总和原表手算各跑一遍,两个数必须相等;再挑一个跨月发货的订单,看它落在哪个月、由哪个外键决定。

症状:客户去重后少了几家,报表上看不出异常;换一台机器部署,同一条 SQL 报 Unknown table。

定位:前者看字符串比较规则,后者看标识符大小写。两件事都发生在字符层面,不在数据本身。

改法:字符串列显式指定 collation。MySQL 8.4 手册 12.3.1《Collation Naming Conventions》写明后缀含义:_ci 表示大小写不敏感、_bin 按码值比较,utf8mb4_0900_ai_ci 基于 UCA 9.0.0 的权重表(12.3.1 节)。差别很实际:客户名在 _ci 下「ABC」等于「abc」,去重会把两家不同主体合成一家。表名则统一小写,手册 11.2.3《Identifier Case Sensitivity》说明库名、表名在 Unix 上区分大小写、Windows 上不区分,macOS 默认的文件系统又不区分,并建议不要用不同大小写引用同一个对象(11.2.3 节)。

验证:同一列分别按 _ci 与 _bin 做一次 COUNT(DISTINCT),两个数不同,说明库里存在仅大小写不同的主体;再把同一句 SQL 在开发机和生产机各跑一遍,报错就说明大小写没统一。

工程师在机房对着屏幕核对两张表的字段
字段对齐这件事,屏幕上永远比嘴上难。

症状:平均值偏低;结果里出现一串空白;某个状态码的订单凭空消失,条数对不上。

定位:看可空列的分布,NULL、0、空串是否在混用;再看状态分布里有没有落在字典之外的值。这两类错都不报错,只在汇总时显形。

改法:约定 NULL 表示未知或不适用、0 表示明确的零,视图层再加一列标记,把「没有记录」和「记了零」分开——不分开,模型会把缺失当零算进平均值。枚举单独建表,不要写在字段注释里:ERP 的状态码会随版本增加,把 0/1/2 的含义写死在提示词里,新增一个状态就全错,而且错得很安静。字典表至少三列,编码、名称、生效起止日期,并指定维护人。

验证:对每个可空列跑一次 NULL 计数,与业务上「没有」的条数对一遍;再查状态分布里未匹配到字典的行数,这个数必须是零,不是「很少」。

四类过完,剩下的是表太多。第三道工序是把它们收敛成视图:按业务域切,订单、库存、物料、客户、财务期间各一组,每个视图有稳定主键、统一时间维,枚举解码成人能读的文本,模型只见视图不见原表。宁可多切几个域,也不要做一个万能宽表——宽表只是把「挑表」换成「挑列」,难度没降,错得更隐蔽。

这就是 dbt 文档 Semantic Layer 一节讲的位置:把指标和语义模型定义在建模层,下游的 BI、查询编辑器、AI 工具都从这一份定义取数,改一次,所有调用处同步(dbt 文档 Semantic Layer)。要借鉴的不是那个产品,是那个位置:模型不该读原表,该读一层收敛过命名和口径的东西。

视图暴露给模型的列要少。原始宽表动辄上百列,视图只留真会用到的那十几列,schema linking 的难度随列数上升,列越少,挑错的机会越少。工具描述只放四样:视图名、列名、中文注释、两个示例查询,不放建表语句,不放全库 DDL——全库 DDL 塞进上下文,还会挤掉真正的问题描述。

视图顺手解决另外两件事。权限:谁能看哪个客户、哪个区域,在视图层用行级条件表达,换一个查询入口也不会漏。口径:含税与不含税、含退货与不含退货、按发货还是按签收确认收入,这些分歧在原始表里无处安放,只能散落在各人的 SQL 里,收进视图后变成可以评审、可以改版本的定义。

也有不该上这套的时候。只查三五张表、字段不到五十个、问题固定,参数化查询比 text-to-SQL 更准也更快,做一个视图加几个模板就够。语义层的成本不在搭,在守:口径一变,字典、视图、回归集三处都要跟着改,没人负责,三个月后它就和真实口径脱节。

还有一种要先停:口径本身还在吵。同一份销售额,两个部门给不出同一个数,且谁也不肯让步。这时候接 AI 只会把争论自动化,模型每天换一个答案,谁都不认。先把口径定下来,再谈接入。

要不要开工,最省一步的验法是这样:随手挑三个业务上真会被问到的问题,用 SQL 手写一遍答案。要翻超过两张表,或者要问人某个字段到底什么意思,这套表结构就还没准备好。三个问题都能在两三张视图里写完,就开始,接入范围只限这三张视图覆盖的业务域。