1. 为什么需要单独梳理一套“常用表清单”
做SAP MM模块时间长了,你会发现一个现实:系统里几千张表,但日常碰来碰去的其实就那几十张。刚接手一个物料管理相关的报表、接口或者增强项目时,最花时间的往往不是写代码,而是在一堆表里翻来翻去找应该用哪张。我见过不少人拿着MSEG一把梭,把物料凭证的所有字段全查出来,结果数据量一大直接把接口拖垮;也见过不少人在月底核对库存金额时,拿着MARD和MCHB两张表对不上数,怀疑程序写错了,其实是没搞清楚这两张表的口径区别。
所以“MM常用数据库表”这种清单,本质上不是在列表名,而是在梳理一张张表之间的数据流转关系。物料主数据在MARA里存基础信息,在MARC里存工厂级属性,到MARD里变成库存数据,发生一笔收货或者发货后会生成MKPF+MSEG凭证,再往后会影响采购订单的EKKO/EKPO,触发发票校验的RBKP/RSEG。这些表就像一条流水线上的工位,搞清楚每个工位干什么、上下游是谁,查数的时候才不会迷路。
这篇文章适合几类人看:刚入行两三年、正在从功能顾问往技术方向靠的人;做报表开发、接口开发时需要频繁查MM模块表的ABAP开发;以及经常需要在HANA视图或者Query里做跨模块取数的数据人员。我会按主数据、凭证、库存、采购、交货这几个维度来拆,每张表都会说清楚用途、关键字段、关联路径和实际踩坑点,不写教科书式的表字典,只写真正用得上的东西。
2. 物料主数据类核心表:从底层说起
2.1 MARA:物料主数据的“总账”
MARA是SAP里物料主数据最基础的一张表,几乎每个物料只有一条记录。它存的是跨所有工厂、所有库存地点都成立的基本字段:物料号MATNR、物料类型MTART、行业领域MBRSH、基本计量单位MEINS、旧物料号BISMT、净重NTGEW、毛重BRGEW、物料组MATKL、外部物料组EXTWG、产品层次PRDHA等。
说它是“总账”是因为后面所有工厂、库存、采购、生产相关的扩展数据,都通过MATNR字段回挂到MARA上。比如我经常写的一个取数逻辑:需要查“物料描述+物料组+基本单位+当前库存”,习惯做法是先打MARA拿到MTART和MATKL,再去MARC拿工厂级数据,最后通过MARD拿库存。这个关联路径里,MARA永远在最底层。
实际使用中特别容易忽略的是MARA的删除标记字段LVORM和物料状态字段MSTAE。这两列经常被报表漏掉,结果就是很多已标记删除的物料仍然出现在报表里,业务看到后一脸懵。另外注意MARA是个数据表,历史变更记录不在这里,要查“这个物料是什么时候创建的、谁创建的”需要去CDHDR和CDPOS看变更凭证,MARA里只有AEDAT和ERNAM这两个直接写入值。
2.2 MARC、MARD、MAKT:工厂、存储、描述的三件套
MARC是物料的工厂级视图表,一条MARA记录在多个工厂下有多个MARC记录。关键字段包括:工厂WERKS、MRP类型MRPPROC(DEJN/PD/P1等)、采购类型SOBSL(E表示外购,F表示自制)、批量大小KEY(DISPO里用)、安全库存EISBE、自动采购订单标记(MABC)、ABC标识MAABC、消耗类字段(如EISLO,等)以及和MRP相关的很多参数。
做MM报表时MARC里最常用的场景是“判断这个物料在这个厂是自制还是外购,是跑MRP还是手动计划”,这个判断往往决定了采购报表要不要统计它、生产报表要不要关联它。MARC还有一张容易混淆的表是MARC历史的替代——别拿它跟MARA搞混,同一物料同一工厂下的MARC行才是唯一的。
MARD是物料的存储地点级库存表,字段包括工厂WERKS、库存地点LGORT、非限制使用库存LABST、质检库存INSME、冻结库存SPEME、在途库存(UNALLOCATED有单独逻辑)、库存总量等。MARD的特点是“物料+工厂+库存地点”维度,它只记录数量,不记录单价金额。要做“库存金额”报表不能只看MARD,必须去结合MSEG和EBEW等相关金额表(但MARD的库存金额一般通过价格控制逻辑、CKML或EBEW处理,见后文)。MARD和MCHB的差异后面会展开讲。
MAKT是物料描述文本表,MATNR+SPRAS+MAKTX存的就是多语言描述。很多人习惯直接Join MAKT拿描述,但要注意两点:第一,MAKT里存在“长描述MAKTX超过40位或者存在特殊字符”的情况,报表导出Excel时经常因为字符问题乱码;第二,不要直接取MAKT的SPRAS不限定中文,特别是做跨境电商或集团汇总时,如果不限定语言,一个物料会带出多条描述,关联结果直接翻倍。这是新手极容易踩的坑。
2.3 MARM、MCH1、MCHB:单位与批次相关表
MARM表存的是物料单位换算关系,字段按“物料+替代单位+基本单位”维度组织。比如一箱是12个,那MARM里会写UMREZ=1, UMREN=12(或者反之)。做采购报表、库存报表时最常碰到的问题就是:数据库里存的数量是基本计量单位,但业务要看到采购单位或者销售单位。不知道或者忽略MARM,报表里数量经常是错的。比如看了库存10箱,以为10件,其实是120件,这个误差尤其在采购订单重量汇总时被放大。
批次的表相对冷门一些,但在启用了批次管理的物料上很重要。MCH1存的是批次抬头(物料+工厂+批次MATNR/WERKS/CHARG),MCHB存的是批次库存(每个工厂、每个库存地点、每个批次的库存数量),和MARD是对等关系,区别是MCHB有批次维度。MCHB的关键字段LABST、INSME、SPEME与MARD一致,但多一个CHARG。很多启用了批次的物料在做库存汇总时,如果只用MARD而不加MCHB,会发现账实不符,因为实际库存是先在MCHB里按批次登记,再做汇总的。
这里分享一个经验:如果客户启用了批次管理,库存报表尽量直接用MCHB做底表,按物料+工厂+库存地点做group by,汇总结果和MARD对得上;如果MARD和MCHB对不上,优先检查这段时间是否有“批次到期日调整”或“批量导入/调整库存”的历史记录,这些操作有时候只更新了MCHB没同步MARD。这个场景我见过好几次,原因各有不同,但基本都是数据不一致,不是程序逻辑错。
3. 库存与物料凭证类核心表:财务和物料的交界点
3.1 MKPF+MSEG:物料凭证的两张关键表
库存的所有移动——收货、发货、转储、盘盈盘亏——都会生成物料凭证,拆成两张表:抬头表MKPF和行项目表MSEG。MKPF存凭证号MBLNR、凭证年份MJAHR、过账日期BUDAT、凭证日期BLDAT、抬头文本BLTXT等;MSEG存行项目,字段非常多,包括物料号MATNR、工厂WERKS、库存地点LGORT、移动类型BWART、数量MENGE、金额WRBTR、关联订单EBELN/EBELP、关联生产订单AUFNR等。
理解MKPF+MSEG的关键在于理解“凭证流水”的概念。一张采购订单收货凭证在MSEG里至少有两条行:一条是移动类型101,更新库存;一条是移动类型102或相关的冲销逻辑。没搞懂移动类型逻辑的人,直接汇总MSEG里某个物料的所有数量,结果数字翻倍或归零都是正常的。正确做法是先过滤BWART,再看是否有反向凭证。
实操上经常遇到的一个场景是:想查“某段时间内某个物料一共收了多少货”,我在项目里一般写这个逻辑:
SELECT MATNR, WERKS, LGORT, SUM( MENGE ) AS TOTAL_QTY FROM MSEG WHERE MATNR = ? " 这里的?是物料号 AND BWART IN ( '101', '102', '105', '106', '161' ) AND MJAHR = ? AND ( 有没有冲销标记, 通过SOLLKZ或相关字段判断 ) GROUP BY MATNR, WERKS, LGORT;这里最容易被忽略的是GRUND(移动原因)和SOLLKZ(贷方/借方指示),以及必须结合MKPF的BUDAT(过账日期)做时间范围过滤。MSEG里没有过账日期,只有MBLNR和MJAHR,所以必须Join MKPF。
3.2 MARD与MCHB:库存表的常见口径差异
上文提到MARD是“物料+工厂+库存地点”的非批次库位视图,MCHB是批次级库存视图,但实际应用中还有第三张表——MSKU是“物料+工厂+特殊库存”维度,比如销售订单库存、项目库存、寄售库存,这类库存不在MARD体现。所以常说“库存不只是MARD”,这三张表分别对应:普通库存、批次库存、特殊库存。
报表里这三个维度经常混在一起出数,不是所有项目都启用了批次,更不是所有项目都用了特殊库存。做库存报表时我很建议先明确范围:你要的库存是包含了寄售库存和销售订单库存吗?如果包含,MARD、MSKU、MCHB三张表要分别取数再合并;如果不包含,只用MARD或者MCHB就行。很多刚开始做MM库存报表的人就是没搞清楚这点,把MSKU漏了,导致月底对不上销售部门的订单库存数,被业务反复投诉。
另外还有一个口径问题:MARD里的LABST是非限制使用库存,但实际“可用库存”还要减去“销售订单预留”和“计划独立需求预留量”。预留数据存在RESB表里,要算真正可用库存,MARD的LABST必须减去RESB按物料、工厂、库存地点汇总出的预留量。这个计算逻辑在R3时代就存在,在ECC和S/4里也一样。我在一个物料齐套分析项目里就遇到过这个坑:按MARD算库存明明够,但生产订单一直不确认,排查下来发现是预留占用了数量,导致MRP认为缺料。
3.3 移动类型与库存金额的关键选择
和库存金额有关的地方需要特别注意,涉及普通库存的金额表有EBEW(工厂视图的评估数据)和EBAN(旧逻辑也不少用到),但实际报表里做库存金额最常用的还是MSEG里的WRBTR与MBGBEW等字段,以及物料分类账的MLHD和MLIT。不同项目采用的成本核算方式不同(移动平均价V或标准价S),取数逻辑差异很大。
移动平均价物料,库存金额直接用“当前库存数量×当前标准价/移动平均价”,实时变动;标准价物料,库存金额按标准价核算,差异进COPA或者统驭科目。如果要写一个“库存金额统计报表”,我建议不要自己去MARD和MSEG做乘法,更稳妥的办法是使用MBEW(物料评估视图)里的VBELN?——不是,MBEW存的是评估类、价格控制、标准价、移动平均价等字段,但它没有库存数量。通常的做法是拿MARD/MCHB的数量乘MBEW当前价格。这个方案有个前提:不能有批次差异,如果有批次价,还得去MCHB和MBEW按批次算。
这块的公式大概是:
评估类 = MBEW-MTVKW ? 其实看项目配置 单价 = 价格控制S ? MBEW-STPRS : MBEW-VERPR 金额 = MARD-LABST * 单价但注意移动平均价(VERPR)是带小数的,直接乘会有精度差异,最好使用SAP函数(如数量换算相关的函数)或者保留小数点后四位。别看这是个细节,月底财务对账的时候,几分钱的差异都能让报表打回返工。预算里的“金额对账”差几分钱是这个模块最烦人的问题之一。
4. 采购与发票校验相关表
4.1 EKKO+EKPO:采购订单抬头与行项目
EKKO是采购订单抬头表,核心字段包括采购订单号EBELN、采购组织EKORG、采购组EKGRP、供应商LIFNR、采购凭证类型BSART(如NB标准、ZNB特殊之类)、单据日期BDATU等。EKPO是行项目表,字段包括采购订单号EBELN、行项目号EBELP、物料MATNR、物料组MATKL、订单数量MENGE、订单单位MEINS、净价NETPR、税码MWSKZ、工厂WERKS、库存地点LGORT、跟踪号TRACKINGNO等。
很多开发会把EKKO和EKPO拆成“抬头为主表、行项目为从表”,用EBELN关联。实际中关键的点不是Join关系,而是BSART和LOEKZ(删除标记)的处理。采购订单一旦做了后续收货和发票,即使LOEKZ打上了删除标记,也不能简单过滤掉,因为历史收货和发票记录还要追溯业务来源。我做过一个采购追踪报表,把所有已删除的采购订单全过滤掉,结果业务发现有三笔历史收货在报表里消失了,对应关系全部断裂,排查起来非常痛苦。
EKPO里还有个容易漏掉的字段——ABSLI(承诺日期/确认交货日期)和EINDT(交货日期),它们和交货计划行没关系,但用来做“是否准时交货”的判断时是关键。做供应商准时交付率分析时,我用的字段一般是从EKET(计划的交货行)取EINDT,从LIKP/VBLI的实际交货单VBELN反过来取WADAT(实际发货日期),两相对比才得到偏差天数。单纯拿EKPO的EINDT或者LIPS的WADAT单独做,结果都会不够准确。
4.2 EKBE+EKET:采购历史与交货计划
EKBE是采购凭证历史表,记录采购订单每一次“收货历史”和“发票历史”,字段包括采购订单EBELN、行项目EBELP、采购历史类型VGDAT——不是,是历史记录类型BEWTP(当时值有RECH=收货、REIN=发票、WARE?),移动类型BWART、数量MENGE、物料凭证倒查用的MBLNR/MJAHR、关联发票凭证号REBELN。这张表在做“采购订单的已收货量/已开票量/未交货量”统计时非常有用,可以避免直接查MSEG再反查EKPO的复杂逻辑。
EKET是采购订单的交货计划表,用于有计划行(Schedule Line)的采购订单。一张EKPO可能对应多条EKET,每行有自己的计划数量(MENGE)、计划交货日期EINDT、以及已被交货的数量(通过EKBE关联)。很多人做“采购订单准时交付率”时只Join EKPO和EKET,结果因为EKET一条计划行对应多次EKBE交货记录,关联出来的数据被放大数倍。正确做法是先对EKBE按EKET行项目做汇总再去Join EKET,或者直接以EKET为主表拉取每条计划行的累计已交数量。
我记得有一个供应商绩效项目,业务要求看“过去12个月某供应商的准时到货率”,我当时的做法是:EKET取计划行EINDT和计划数量,EKBE取按计划行累计的交货数量和非限制库存的收货记录,最后用一个字段判断EINDT和实际收货日期WADAT的差值,算准时率。这个方案数据处理量大,但逻辑清晰,业务后来还基于它做了月度考核看板。
4.3 RBKP+RSEG:发票校验的凭证结构
发票校验生成的凭证不是MSEG,而是财务会计凭证加专门的发票凭证表:RBKP是发票抬头,RSEG是发票行项目。RBKP关键字段:发票凭证号BELNR、会计年度GJAHR、过账日期BUDAT、供应商LIFNR、发票总金额RBWRT、税金额(通过MW史料?)、冲销标记STBLG等。RSEG关键字段:行项目号BELNR+BUZEI、采购订单EBELN、行项目EBELP、数量MENGE、金额WRBTR、税额MWSKZ等。
做“三单匹配”核对(采购订单、收货单、发票)时,最常用的逻辑就是拿RSEG和EKBE或者MSEG的关联。需要注意RSEG的BELNR和GJAHR必须同时传给RBKP才能定位抬头;发票大量通过MIRO或BAPI(如BAPI_INCOMINGINVOICE_CREATE末尾)录入,有些项目用电子发票集成,RSEG里的EBELN可能为空(尤其是总账科目行),取数的时候要做空值处理。
另外不要忽略RBKP的BLDAT(凭证日期)和BUDAT(过账日期)的区别,月末验收发票时,业务经常在凭证日期上含糊,导致报表里“发票本月多少、下月多少”分不清。我处理过一个案例:财务月底关账后,系统里还有一批上月录入的发票未过账,BUDAT已是次月,但BLDAT还是上月末,业务想在上月报表体现,最后实际方案是用RFBLG规则或者BKPF去映射统驭科目,而不是简单按BUDAT查RBKP。
5. 交货与物流执行相关表
5.1 LIKP+LIPS:外向/内向交货单
涉及发货、收货、运输的流程,离不开LIKP和LIPS两张表。LIKP是交货单抬头,字段包括交货单号VBELN、交货类型LFART、发货工厂(WERKS用于内向/外向逻辑中不一样)、收货方KUNNR、发货单日期LFUHR?——实际上是WADAT(实际货物移动日期)、装运条件等。LIPS是交货单行项目,字段包括交货单号VBELN、行项目号POSNR、物料MATNR、数量LFIMG、单位VRKME、订单数量等。
和MSEG相比,LIKP/LIPS更偏物流执行层面,MSEG更多是财务/物料账层面。查“这张销售订单实际发了多少货”时,是去LIPS按VBELN查,而不是直接去MSEG查移动类型601的记录——虽然MSEG关联LIKP也能达到类似效果,但LIPS本来就是服务于发货查询的。如果是做外向交货的包装和装运信息,还需要关联VEPO(包装对象)和VBOX(装运单元),这些在医药或快消行业的装箱追溯需求中很常见。
需要注意LIKP的删除标记LFART或者记录删除状态——不是,LIKP有字段LOEK,另外还有“已过账”状态字段(一般用WFGRP或相关的状态?),当交货单已做PGI(Post Goods Issue)后,LIPS里数量不会变,但MSEG里会生成对应的发货凭证。做“发货差异”检查时,要同时看LIPS数量和MSEG的对应记录数量,两个数量不一致往往体现为“交货单已确认但物料凭证未生成”或反之,这种情况在接口集成项目里经常遇到。
5.2 T001L与T001W:工厂和库存地点的配置表
最后聊两张配置表,它们本身不是业务数据表,但几乎所有MM报表都要用。T001W存工厂主数据,字段包括工厂WERKS、工厂名称NAME1、国家LAND1等;T001L存库存地点,字段包括工厂WERKS、库存地点LGORT、库存地点描述LGOBE等。
个人经验是,在做接口或报表时,把这两张表的数据先加载进内存或者同步到查询视图,比每次实时关联性能好得多。因为MARC、MARD、MSEG里都有WERKS和LGORT,如果每行都去关联T001W/L,数据量一大性能立刻下降,尤其是HANA上虽然快,但做细节行报表时Join次数多也很耗资源。我一般会先取出工厂和库位的清单,用内存表去做走查,最后再补描述。
还有一个容易忽略的关联:MARD和MCHB里的库存地点如果是“虚拟库位”或者“未分配库位”,这些库位在公司代码里往往不可见,但业务期望能看到。所以做库位维度的分析时,尽量把MARD和MCHB都拉出来,再和T001L关联,保留未匹配的库存地点行,避免数据被过滤掉。
6. 实际排查中的几个高频问题
6.1 凭证找不到对应物料怎么办
一个常见情况是:用MSEG查某段时间的物料凭证,发现里面大量记录MATNR为空。这通常发生在“总账科目过账”或“费用类采购”场景,没有物料维度,只有科目和金额。如果业务硬要在物料凭证里找物料,那就会漏掉这一部分数据。处理这种问题时,可以把MSEG的MATNR为空的行按KOART(科目类别)和HKONT(总账科目)汇总,再配合EKPO和RSEG的关联看采购内容,通常就能还原业务原貌。不要一上来就觉得数据有问题,这个设计本身就是合法的。
还有一种情况是物料凭证已经冲销,但业务在报表里看不到原凭证号,这是因为冲销凭证和原凭证之间的关联字段是STBLG(冲销凭证号)+STJAH(冲销年份),不是简单原MSEG行里能找到的。那时候就要先找到原凭证号,再反查冲销凭证,或者反过来。我自己处理过一条差旅费转移过账:原凭证没删除,业务却看不到,最后发现是冲销凭证把金额抵掉了,因为没有排除冲销标记,报表显示的是净额但没有说明。
6.2 库存报表金额差异的排查思路
关于“库存金额对不上”这个经典问题,我现在的排查顺序是:
- 先确认口径:是否包含批次库存?是否包含特殊库存?是否包含在途库存?
- 再确认价格:用标准价还是移动平均价?价格维护在MBEW的哪一列?
- 然后核对数量:MARD/MCHB的数量和MSEG的累计移动量是否一致
- 最后检查期间:财务关账后是否做了物料账重估,导致期初金额和期末金额不平
这几个环节里最容易出问题的其实是第一个。很多企业启用了“销售订单库存”和“项目库存”,这些库存不进MARD,但财务总账里已经计入存货。如果报表只取MARD,再和总账核对,永远对不上。方案是额外把MSKU的金额也加进来,或者直接用物料分类账的MLIT来做。这个表按物料/工厂/期间记录了期初、收入、发出、期末数量和金额,是财务库存台账的对账基准。想在MM表和FI总账之间做核对,MLIT是桥梁。
6.3 从MSEG反查采购订单和交货单的关系链
MSEG里和采购相关的关键字段是EBELN(采购订单号)和EBELP(行项目号),交货单相关字段是VBELN(交货单)和VBELP(交货行项目)。如果一张物料凭证是由采购收货生成的,MSEG里的EBELN会有值;如果是外向交货PGI生成的,MSEG里的VBELN会有值。用这两个字段可以直接反查EKKO/EKPO或LIKP/LIPS。
但有一个坑:MSEG里对于移动类型101收货,WERKS和LGORT可以直接取到,但某些特殊库存(如Q库存、S库存)需要和EKPO关联才能判断库存归属。我做过一个追溯项目,要查“某批物料是由哪个供应商、哪个采购订单、哪个交货单送进来的”,完整链路是:MSEG(MATNR+MBLNR) 反查 EKBE/EKPO 得到 EBELN+EBELP,再通过 LIKP 的 VGBEL 反查交货单,再通过交货单反查装运信息。这个链路能解决很多质量追溯问题,但也是新手最没头绪的地方。
7. 日常开发中的几点个人体会
最后分享几个我实际工作中积累的建议。先把主数据表和单据表分开建视图,比如单独建物料主数据视图(MARA+MARC+MAKT)、库存视图(MARD+MCHB+MSKU)、凭证视图(MKPF+MSEG+RSEG),用到什么业务场景直接去对应视图层取数,比每次从头写Join清晰很多。而且S/4 HANA里,这些视图做过底层合并之后,性能比在ECC里好很多,但前提是查询条件要给足,不要把MSEG整表扫了再过滤。
另一条经验是维护一套“表字段映射手册”。MM模块的表字段命名是有规律的:几乎都是抬头主键EBELN、行项目EBELP、物料MATNR、工厂WERKS、库存地点LGORT、数量MENGE、金额WRBTR。这个规律看似简单,但跨表Join时非常管用。遇到新表先看主键结构,再找这几个标准字段,马上能定位它属于哪个业务层级。
再有就是做任何和金额相关的报表,一定要和财务确认“取数期间”。MM模块的库存凭证在过账日期上是灵活可调的,如果财务月结前有一批凭证过账在次月,那你上月的库存报表就会少一块。多数时候需要结合MKPF的BUDAT和会计期间(通过COBL或者BKPF映射)一起判断,而不是只凭MSEG里的MJAHR。别问我是怎么记住这条的,都是踩过坑换来的。
把常用表梳理成自己的知识体系之后,你会发现做MM相关的开发、排错、接口对接,速度会快很多。至少我后来做需求分析时,基本不用去系统里翻“哪张表存什么”,直接就能告诉业务:这个数可以从哪几张表取、怎么关联、可能有什么口径风险。这种感觉,比记一百张表名有用得多。