简介:面向E10 ERP管理员的常用SQL语句集合,聚焦系统上线后的持续改善场景,覆盖PO待交明细、出入库统计、待领料清单、请购单价检查、物料采购分析、呆滞料计算、应付已付汇总、订单达交率与工单准时完工率等核心业务分析查询。压缩包内共包含50个文件,以46个SQL脚本为主体,辅以DOC/DOCX操作说明和PDF逻辑手册,包体仅1.67MB,便于下载与查阅。目前已有1184人学习,适用于需要借助SQL深入核查数据、优化流程的ERP管理人员。除常用分析查询外,还提供覆盖品号、仓库、工作中心、工厂信息、采购与销售信息、客户品号、工艺路线、供应商料件价格等主数据导入的后台脚本,并附存储过程与逻辑说明文档,可帮助管理员快速搭建初始化数据、减少重复手工处理。 做 ERP 实施这几年,我最常被业务方问的一句话不是“系统怎么又报错了”,而是“帮我在数据库里查一下这个数”。E10 这类 ERP 系统,前台画面能覆盖日常八成的操作,但剩下两成,比如跨模块对账、历史数据追溯、异常单据定位、字段取值核查,基本都得靠 SQL 直接查后台。我刚开始接手 E10 项目时,光是找表就花了不少时间,后来慢慢把日常高频查询整理成一套“查用 SQL 语句集合”,按场景分类,遇到问题直接翻开抄,效率提升非常明显。这篇就把我自己项目里沉淀下来的 E10 查询脚本、表结构规律、排查思路和踩坑经验一起分享出来,适合正在做 E10 实施、运维或二开的同学参考。
1. 为什么我坚持维护一份“查用 SQL 集合”
1.1 图形界面解决不了的三类问题
E10 的前台报表和查询画面做得并不差,但实际项目里总会遇到三类问题,光靠点鼠标很难定位。
第一类是数据对不上。库存报表和账面数差了几百件,前台能看到总数,但不知道差异到底来自哪笔单据;订单交期被改了,业务想知道改之前的值是什么,前台画面早就覆盖掉了。第二类是流程卡住。单据卡在某个审核节点,前台只显示“等待审核”,但具体是谁、哪个角色、为什么没流下去,前台查不到,只能去后台单据表、审核记录表里翻。第三类是误操作修复。品号资料被重复建立、单号断号、BOM 被覆盖,这些情况需要把原始数据捞出来比对,确认到底是操作问题还是程序问题。
这三类问题都有一个共同点:必须通过 SQL 站在数据层面去还原现场。前台是业务视角,后台才是数据真相,手上有一套按场景整理好的脚本,几分钟就能定位,比现场临时翻表靠谱得多。
1.2 这套集合的定位和使用边界
我整理的这套 SQL 集合,定位非常明确:以查询为主,主要解决“数据在哪里、怎么取、怎么判断”。它不是让你拿去直接改数据的,虽然查询结果往往也会暴露出需要修正的数据,但修正操作是另一套流程,必须经过确认、备份、走改数申请。
刚开始接触 E10 的同学可能会有一个误区,以为拿到一套 SQL 就能到处跑、什么表都能查。实际上每套 E10 项目的库表结构多少都有差异,版本不同、二次开发程度不同,表名和字段名都可能不一样。所以我的脚本集合在开头就写清楚了表名和字段名的来源环境,方便后续按实际库调整。这也是我一直强调的:查询脚本只有建立在理解业务和表结构的基础上,才真正有用,否则就是一堆无法落地的代码片段。
2. 先摸清 E10 的库表规律,查表不再靠猜
2.1 看命名规则,少走一半弯路
E10 的数据库结构,我用了几套项目下来,最大的感受是规律性很强。无论前端界面叫什么名字,后台表基本都遵循几条约定。
主档表和交易表分开。品号、客户、供应商、仓库这类基础资料,一般在独立的主档表里,表名和字段相对稳定;而订单、工单、出入库单这类交易数据,会拆成单头和单身两张表,单头存公共信息,比如单号、日期、客户、审核状态,单身存明细信息,比如品号、数量、单价、仓库。关联的时候用单号加单身序号,这个结构非常像 Excel 里的“订单主表+订单明细表”拆分,只是强制用外键关联。
关联字段也有规律。品号字段通常是 itm_no 或 item_no,单号通常是 xxx_no,日期通常是 xxx_date,数量通常是 qty 或 qty_xxx。单身表一般会有一个序号字段,比如 seq 或 itm_seq,配合单号做联合主键。另外,E10 还提供了一批视图,名字往往以 V_ 或 View 开头,视图里已经帮我们把常用关联做好了,日常查询优先用视图,能省掉大量 JOIN 操作。
2.2 我日常高频使用的核心表清单
下面这张表是我个人项目里高频使用的表结构示例,属于简化后的通用模型。不同环境可能有差异,但定位思路是一致的:
| 表名(示例) | 类型 | 主要用途 | 常用关联字段 |
|---|---|---|---|
| item_base | 主档 | 品号基本资料,品名、规格、状态 | itm_no |
| item_cat | 主档 | 品号分类,如原材料、半成品、成品 | cat_no |
| cus_master / sup_master | 主档 | 客户主档、供应商主档 | cus_no / sup_no |
| bcm_master / bcm_detail | 单头+单身 | BOM 表头与 BOM 单身 | bcm_no |
| cms_order / cms_order_d | 单头+单身 | 客户订单及订单明细 | ord_no |
| moct_master / moct_detail | 单头+单身 | 工单/制造令及工单明细 | mo_no |
| wh_master | 主档 | 仓库档案 | wh_no |
| inv_balance | 交易 | 库存结余,按仓库+品号存放数量 | wh_no + itm_no |
| inv_trace | 交易 | 出入库流水,记录了每笔异动 | doc_no + itm_no |
| V_ITEM_STOCK(视图) | 视图 | 常用于直接查库存、可用量 | itm_no / wh_no |
实际使用中,我会先用系统自带的表字典或数据库工具大致浏览一下表清单,把这种“主档表、单头单身表、汇总账表、流水账表”的分类框架搭出来,后面不管碰到什么新查询,都能快速判断应该去哪一层取数。就比如查库存,先想清楚是要看当前结余(汇总表)还是看每一笔出入库(流水表),这决定了 SQL 怎么写。
3. 高频查询场景与 SQL 参考
3.1 基础资料查询:品号、BOM、客户供应商
基础资料查询是最常见的,一般用于核对档案是否建重复、状态是否启用、BOM 用错料号这类问题。先看品号基本资料查询:
SELECT i.itm_no AS 品号, i.itm_name AS 品名, i.itm_spec AS 规格, i.status AS 状态, c.cat_name AS 分类 FROM item_base i LEFT JOIN item_cat c ON i.cat_no = c.cat_no WHERE c.cat_name = '原材料' AND i.status = '1' ORDER BY i.itm_no;需要注意,状态字段在不同版本里可能用 1/0、Y/N 或者 A/V/I 来表示,不要想当然。我第一次写就是默认状态 1 代表有效,结果把一批停用品号全带出来了,还好只是自查。稳妥的办法是先 SELECT * 出来看一两行,确认字段含义再正式写查询。
BOM 是制造业项目里最容易出问题的资料,查 BOM 时最核心的是把“单头+单身+品号名称”三层关系拉平。简化示例如下:
SELECT bm.bcm_no AS BOM编号, pm.itm_no AS 母件品号, pm.itm_name AS 母件品名, bd.itm_no AS 子件品号, bd.qty AS 单位用量, bd.unit AS 单位 FROM bcm_master bm JOIN item_base pm ON bm.parent_itm_no = pm.itm_no JOIN bcm_detail bd ON bm.bcm_no = bd.bcm_no WHERE bm.bcm_no = 'BOM20240001' ORDER BY bd.seq;这类查询的关键是理解“母件-子件”关系。BOM 单据里,单头存的可能是 BOM 编号、版本、母件品号,单身存的是子件品号和用量,一定不要把母件和子件的品号搞反了。查出来之后,还可以把它和标准成本、替代料等表继续关联,但新手阶段先把这张“平铺明细表”做好,后面怎么加都顺。
3.2 单据查询:订单、工单、出入库流水
单据查询的场景更多是“业务跟我反映某个单有问题,我要把整单信息拉出来看”。以客户订单为例,单头信息如订单号、客户、订单日期、审核状态,单身信息如品号、数量、单价、交期,全都放到一个结果集里:
SELECT o.ord_no AS 订单号, o.ord_date AS 订单日期, o.status AS 状态, c.cus_name AS 客户, od.itm_no AS 品号, od.qty AS 数量, od.price AS 单价, od.require_date AS 要求交期 FROM cms_order o JOIN cus_master c ON o.cus_no = c.cus_no JOIN cms_order_d od ON o.ord_no = od.ord_no WHERE o.ord_date >= '2024-01-01' AND o.status IN ('1', '2') ORDER BY o.ord_no, od.seq;订单查询最容易掉的坑,就是 JOIN 之后没有注意单头对单身是一对多。如果单头 JOIN 单身时把条件写漏了,或者关联字段错误,行数会成倍翻。我在 5.1 里会专门说这个现象。这里先记住一个自查习惯:写完带 JOIN 的查询后,先看返回多少行,再用单头表的单号数量对比一下,如果结果行数比单号数量多很多,大概率是 JOIN 条件有问题或表关系没理清。
工单或制造令的查询结构和订单几乎一样,只是把订单表换成 moct_master / moct_detail,关联字段换成工单号。出入库流水也是一样思路,但要注意流水表往往有“单号+单别+品号”这种复合关系,同一个品号在同一天可能有多笔异动,查流水时务必带上仓库和异动类型字段,否则很难定位具体是入库还是出库、是调拨还是盘点产生的。
3.3 库存与在途:把“量”拆清楚再写 SQL
库存相关查询是 E10 项目里频率最高的,也是最容易闹出“取数口径”问题的。我先列出两类常见需求:一是查当前库存结余,二是查在途订单。库存结余适合用汇总表,因为在途量是用订单单身没入库的部分去推算的。
SELECT w.wh_name AS 仓库, i.itm_no AS 品号, i.itm_name AS 品名, CONVERT(DECIMAL(18,3), SUM(b.qty)) AS 现有量 FROM inv_balance b JOIN item_base i ON b.itm_no = i.itm_no JOIN wh_master w ON b.wh_no = w.wh_no WHERE b.qty <> 0 GROUP BY w.wh_name, i.itm_no, i.itm_name ORDER BY w.wh_name, i.itm_no;查询结果集里保留品号和品名,这个动作看起来多余,但在后续核对 Excel 时非常有用,少了品名,层层透视的时候就只能看到一串品号,非常痛苦。
如果想看某个品号的“可用量”,往往要结合在途量、预约量、冻结量等字段计算,不同项目的口径不同。我的建议是:先向业务确认口径,再对着字段写公式,不要自己编一个“安全库存=现有量-在途量”拍脑袋定义。口径错了,SQL 再漂亮也是白搭,业务方拿你查出来的数去开会,结果对不上,信任感一次就没了。
4. 让查询不卡壳的几个优化习惯
4.1 慢查询排查:先看执行计划,再谈优化
E10 项目一般都装在 SQL Server 上,库表数据量上去后,偶尔会遇到一个 SQL 跑几十秒甚至几分钟。这时候不能凭感觉加索引,而是先用执行计划定位。SQL Server Management Studio(SSMS)里点“包括实际执行计划”再执行查询,结果下方会出现执行计划图,重点看几个地方:有没有大表扫描(Table Scan/Clustered Index Scan)、有没有警告图标(比如缺失索引提示)、各个操作符的“估计行数”和“实际行数”偏差大不大。
我习惯的排查顺序是:先看能不能缩小 WHERE 范围,比如时间字段别写一个特别宽的范围、别在条件里对字段做函数运算(比如 WHERE YEAR(date_col)=2024 会抑制索引使用);再看 JOIN 的关联字段类型是否一致,类型不一致极容易导致隐式转换进而全表扫描;最后才考虑加索引。我自己踩过的坑是日期字段存成了 varchar,写 BETWEEN 时看起来能查出来,但完全走不了索引,后来统一转成日期类型再比较,查询时间从几十秒降到了几百毫秒。
4.2 去重、空值与窗口函数的正确用法
热门搜索词里经常出现“SQL 语句去重”“SQL 去除空值”“SQL 窗口函数”,这几个技巧在 E10 查询里确实很常用。先说空值,库存表、价格表经常有空字段,处理时我优先用 ISNULL 或 COALESCE,而不是在 WHERE 里写字段 = '',因为 NULL 和空字符串完全不是一回事。比如查品号价格时,没维护价格的品号可能为 NULL,直接用 ISNULL 把它转成 0 再比较,结果才完整。
窗口函数是处理“取最新一笔”“按品号排名”这类需求的最佳选择。比如想找每个品号最近一次变更的价格记录:
SELECT itm_no, price, effective_date FROM ( SELECT itm_no, price, effective_date, ROW_NUMBER() OVER (PARTITION BY itm_no ORDER BY effective_date DESC) AS rn FROM item_price ) t WHERE t.rn = 1;这套写法比 GROUP BY 灵活得多,因为 GROUP BY 只能取分组字段和聚合函数结果,想额外带出价格、生效日期这些明细字段就会很别扭。而 ROW_NUMBER() 唯一标识出同一品号下按日期排序的第几条记录,再取 rn = 1,干净利落。类似的场景还有查找重复品号、重复 BOM 版本等,都可以套这个模板。
至于去重,我只会用 DISTINCT 处理那种“探索性查询”,正式跑数的 SQL 尽量避免它,因为 DISTINCT 往往是掩盖 JOIN 重复问题的偷懒做法。如果查出来有重复,先找清楚重复的根源到底是从哪张表扩出来的,而不是图省事加个 DISTINCT,否则这次是去重了,下次换一个条件又错。
5. 我踩过的坑:常见问题与排查实录
5.1 JOIN 查询结果翻倍
最典型的一次,是业务反馈“某订单单身明明只有 3 行,查出来却有 12 行”。我顺手把 SQL 里所有 JOIN 的表列出来,发现除了订单头、订单单身外,还关联了客户主档和品号主档。客户主档和品号主档本应是一对一关系,但客户表里有重复档案,品号表里也有一模一样的品号重复建档,结果 1(订单)对 3(单身)再对 2(重复客户)再对 2(重复品号),行数直接乘到了 12。
排查方法很简单:不 JOIN 客户和品号表,先只看单头+单身,确认基础行数是 3;然后一次加一个 LEFT JOIN,每加一个就观察行数变化,在哪一步翻了倍,就是哪张表出了问题。这个经验后来我整理成一条法则:JOIN 之前先确认每个关联字段在关联表里是不是唯一的,不对应的先聚合好再关联,尤其是在主档表上有重复资料时,不要直接 JOIN。
5.2 类型不一致与隐式转换
有段时间我写库存查询特别慢,反复优化索引都没效果。后来查看执行计划,发现 WHERE 条件里,字段是 varchar,但参数我写成数值型,SQL Server 自动做隐式转换,导致该字段的索引失效。具体现象是:条件写得越精确,跑得越慢。后来统一用 CONVERT 或 CAST 把参数显式转成字段类型,或者把字段重建为正确的数据类型,问题才彻底解决。
这个坑最容易出现在日期和单号字段上。E10 里有的版本日期字段是 datetime,有的是 varchar,单号字段基本是 varchar,但导出到 Excel 后常被转成数值,回查时如果用数值去匹配单号,就会出现查到一半查不到的情况。我的习惯是:任何外部输入的值,先在查询前统一处理成明确的字符串,再进 SQL,能避免一大半诡异问题。
5.3 排序规则冲突与跨库查询
E10 项目有时候会把报表库和正式业务库分开,跨库查询就成了常事。跨库 JOIN 时如果两个库的排序规则不一致,会直接报“无法解决排序规则冲突”。第一次碰到时我以为是自己 JOIN 条件写错了,检查半天发现是库级别排序规则不同。
解决方案不复杂:关联字段比较时,在字段后面加 COLLATE DATABASE_DEFAULT,强制使用当前库默认排序规则去比较。比如:
SELECT t1.itm_no, t2.itm_name FROM db_bak.dbo.item_old t1 JOIN item_base t2 ON t1.itm_no = t2.itm_no COLLATE DATABASE_DEFAULT;另外还要提醒一下,跨库、跨服务器查询前,先确认账号是否有权限,E10 生产库的账号一般不会给你太多权限,不要尝试用 sa 账号连接,风险极大,出了问题说不清楚。
5.4 安全底线:查询语句别做成万能输入口
最后说一个我给自己定的规矩。哪怕是查询脚本,也绝不直接拼接外部输入值。很多同学喜欢把 SQL 集合做成带参数的功能,比如在 Excel 里输一个品号就自动生成查询,这个方向很好,但实现时一定要用参数化查询,而不是把用户输入直接拼进 SQL 字符串里。如果直接把输入值拼接进查询条件,等于把 SQL 注入的风险亲手送到业务手里,一旦有人输入特殊字符,轻则查询报错,重则数据遭殃。
注意:凡是把你整理好的 SQL 分享给别人使用时,一定要用参数化查询、只读账号、最小权限,并明确告知这些脚本只用于查数,不做任何更新删除操作。
这四个坑只是我踩过的一小部分。做 E10 数据库查询几年下来,最大的心得不是背了多少表名,而是建立了“先理解业务口径,再梳理表关系,最后写 SQL”的思维路径。每次从排查中写出一个新的查询脚本,我会顺手在文档末尾追加一段备注,写上库环境、执行时间、结果行数和踩坑点,时间一长,这套东西就不只是脚本集了,更像一个项目知识库。平时带新人我也直接把这个文档丢过去,让他们先照着查几天数,基本就能独立应付大多数业务查询需求。如果你也在维护 E10 或其他 ERP 系统,建议从今天就建一个这样的查用 SQL 集合,哪怕只有十条八条,也比每次临时翻表强得多。
本文还有配套的精品资源,点击获取