简介:建材物资管理信息系统数据库设计文档,是一份面向数据库原理课程设计或建材行业管理系统开发的参考资料,可帮助读者掌握从数据库原理到实际表结构落地的完整流程。文档覆盖外部设计、概念结构设计、逻辑结构设计与物理结构设计,包含系统整体E-R图、关系图,以及物资信息表、客户信息表、管理员信息表和员工信息表等核心表结构定义,同时给出存储过程、触发器、视图脚本及数据库恢复与备份方案。资源包共1个文件,为PDF文档,大小472KB,已有190人学习。无论是计算机专业学生完成课程设计,还是开发建材物资管理系统的技术人员,均可对照其中的表字段说明与SQL脚本来规范自身数据库设计,减少数据冗余并提升系统安全性与稳定性。
1. 建材物资管理信息系统的数据库设计:为什么说它决定了系统上线后翻不翻车
建材行业的物资管理系统,表面看就是一套进销存,可真做起来,数据库设计反而是最容易让人熬夜的部分。我接过几个建材贸易公司的项目,业务方一开始都觉得“不就是管管进货、卖货、库存嘛”,结果一跑起来全是幺蛾子:水泥按吨进、按袋出,钢材按根买、按吨卖,同一批砖分三个工地领用,月底对账怎么都平不了。这些问题的根源,几乎都出在设计阶段没把表结构和业务流程对齐。所谓“建材物资管理信息系统数据库设计”,核心就是把供应商、客户、建材字典、仓库、单据、库存流水这些实体用关系模型组织清楚,让每一笔业务都能追溯到单据、能对上账。这篇笔记面向要独立设计这类系统的开发者和实施人员,按照“业务模型 → 库存模型 → 建表落地 → 踩坑排查 → 验收对账”的顺序,把一套能落地的方案讲透。
2. 业务分析先行:把建材行业特有的实体关系理清再动手建表
2.1 供应商、客户与建材字典:主数据建模的取舍
做数据库设计最容易犯的错,是一上来就打开 Navicat 建表。我一般会先在纸上把业务里的“主数据”和“单据数据”分开。主数据包括供应商、客户、仓库、建材字典,单据数据包括询价单、采购订单、入库单、出库单、结算单。建材字典这块最特殊,它不能像普通商品表那样铺成一张大平面表,否则“螺纹钢 HRB400E 12mm”和“螺纹钢 HRB400E 14mm”会被当成两条毫无关系的记录,统计库存和价格时非常痛苦。
常见做法是把建材字典拆成两级:品类和具体物料。品类管“型材、板材、水泥、砂石、管件”,物料管“品牌 + 规格 + 型号 + 默认计量单位”。这样做的好处是,报表可以从品类一层往上汇总,不需要在物料编码上做模糊匹配。供应商和客户则要额外记录信用额度、结算方式、联系人,这些字段在后续做应付应收时会频繁使用。
CREATE TABLE material_category ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, category_code VARCHAR(16) NOT NULL COMMENT '品类编码', category_name VARCHAR(32) NOT NULL COMMENT '品类名称,如型材', parent_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '上级品类,0为顶级', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1启用 0停用', PRIMARY KEY (id), UNIQUE KEY uk_category_code (category_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='建材品类表'; CREATE TABLE material_dict ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, material_code VARCHAR(32) NOT NULL COMMENT '物料编码,业务查询用', category_id INT UNSIGNED NOT NULL COMMENT '所属品类ID', brand VARCHAR(64) NOT NULL DEFAULT '' COMMENT '品牌', specification VARCHAR(64) NOT NULL DEFAULT '' COMMENT '规格’,如 HRB400E/12mm', model VARCHAR(64) NOT NULL DEFAULT '' COMMENT '型号', default_unit VARCHAR(16) NOT NULL COMMENT '默认计量单位:吨/平方米/立方米/根/块', purchase_price DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '参考采购价', sale_price DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '参考销售价', status TINYINT NOT NULL DEFAULT 1, PRIMARY KEY (id), UNIQUE KEY uk_material_code (material_code), KEY idx_category_brand_spec (category_id, brand, specification) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='建材字典表';这段建表 SQL 看起来简单,实际有两个关键决策。一是用UNIQUE KEY uk_material_code锁住物料编码,业务系统里扫码、导入、对账都依赖这个编码,一旦允许重复,后面所有单据都会串。二是把category_id单独拿出来建索引,目的是让常见的“按品类汇总库存金额”走索引扫描,而不是全表过滤。specification字段建议不要拆成“直径/长度/壁厚”多个字段,建材行业规格描述非常口语化,拆太细反而让录入人员崩溃,一个文本字段加规范录入校验是更务实的方案。
2.2 合同、订单、出入库单据:流程单据怎么拆才能对得上账
主数据定好后,要梳理的是单据流程。建材业务的典型链条是:谈合同 → 下采购订单 → 到货入库 → 供应商开票结算;销售侧是:客户询价 → 销售订单 → 发货出库 → 客户签收 → 回款对账。这里最忌讳的是把“订单”和“出入库单”混在一张表里,一个字段叫“单据类型”来区分。混表设计在查询历史单据时确实方便,但一旦出现部分收货、分批发货、退货冲红,状态机复杂到根本写不清楚。
我习惯把每类业务拆成“主表 + 明细表”两张表,主表存单据号、往来单位、业务日期、制单人、审核状态,明细表存物料、数量、单价、金额、备注。主表和明细表通过单据 ID 关联。这样拆的好处有两个:一是明细可以自由增删行,不影响主表状态;二是未来做库存流水时,只需要从明细表读取数量单价,不需要 JOIN 主表拿冗余字段。
单据编号建议在程序里生成,不用数据库自增 ID 直接展示给用户。建材行业的单据号有严格的连续性要求,财务对账、内部审计都盯着这个号。用自增 ID 的好处是快,但一旦删除记录就会断号,且自增 ID 很容易在数据迁移时冲突。比较稳的做法是用“日期 + 仓库/部门编码 + 流水号”拼字符串,比如PO20250612001,并在主表上建唯一索引兜底。流水号生成要考虑并发,常见做法是在程序里对当天单号加锁,或者用一个单独的单号序列表来生成。
3. 库存模型是建材系统的命门:批次、移动加权与数量精度
3.1 为什么建材库存不能只记一个总数量
很多做惯普通电商库存的开发者,会把库存设计成warehouse_id + material_id + qty三个字段。这在卖标准品的场景没问题,但在建材行业会直接翻车。原因有两个:批次和计量单位。同一款水泥,不同批次进货价不同,出库时如果只记总数量,成本根本算不准;同一型号钢材,分属不同采购批次,客户退货时你得知道退的是哪一批的货,否则库存账实不一致。
所以建材物资系统的库存核心不是“余量表”,而是“流水表”。库存余量只是流水按物料、按批次累加出来的投影。任何入库、出库、盘点调整、移库,都先写流水,再更新余量。这种设计被称作“流水驱动余额”,它能保证每一笔库存变动都可追溯。坏处是查询当前库存需要聚合流水,所以一般会保留一张stock_balance实时余量表,通过事务同时更新流水和余量,用余量表应对高频查询。
3.2 用出入库流水加批次表实现可追溯的库存账
具体落地时,我通常会建两张表:stock_transaction存流水,stock_balance存实时余量。流水表的核心字段包括仓库、物料、批次号、业务类型、入库数量、出库数量、单价、来源单据。这里的biz_type是区分入库、出库、退货、盘点、移库、期初的字段,必须用数字字典管理,不能在程序里散落魔法值。
CREATE TABLE stock_transaction ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', material_id BIGINT UNSIGNED NOT NULL COMMENT '建材字典ID', batch_no VARCHAR(32) NOT NULL DEFAULT '' COMMENT '批次号,如采购单号或生产日期批号', biz_type TINYINT NOT NULL COMMENT '10采购入库 20采购退货 30销售出库 40销售退货 50盘点调整 60移库 70期初', qty_in DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '入库数量,出库时填0', qty_out DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '出库数量,入库时填0', unit_cost DECIMAL(18,6) NOT NULL DEFAULT 0 COMMENT '变动单价,成本单价或销售单价', source_order_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '来源单据明细ID', remark VARCHAR(255) NOT NULL DEFAULT '', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_by BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '操作人ID', PRIMARY KEY (id), KEY idx_wh_mat_batch (warehouse_id, material_id, batch_no), KEY idx_source_order (source_order_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表'; CREATE TABLE stock_balance ( warehouse_id BIGINT UNSIGNED NOT NULL, material_id BIGINT UNSIGNED NOT NULL, batch_no VARCHAR(32) NOT NULL, qty DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '当前结存数量', total_cost DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '当前结存总成本', version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', last_trans_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '最近一笔流水ID', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (warehouse_id, material_id, batch_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存余量表';有几个参数需要特别说明。qty_in和qty_out拆成两个字段,而不是用一个正负号数量字段,目的是让 SQL 汇总时SUM(qty_in) - SUM(qty_out)可以直接书写,不需要 CASE WHEN 判断方向,效率更高也更容易读。unit_cost用了DECIMAL(18,6),而总额是DECIMAL(18,4)。理由是建材有的单价小数位非常多,比如钢材按吨计价,实际金额需要保留到分,单价多保留两位能减少累计误差。version字段是乐观锁,更新余量时用UPDATE stock_balance SET qty = qty + ?, version = version + 1 WHERE warehouse_id=? AND material_id=? AND batch_no=? AND version=?,这样可以避免两张单据同时扣同一批次库存导致超卖。
关于批次号生成,建材行业通常用“入库单号”或“供应商发货单号 + 到货日期”。不要用系统自增 ID 当批次号,因为仓库堆头上贴的标签、质检报告上的批号,必须能跟系统对上。批次表里还应该存生产日期、保质期、供应商批号等信息,用于处理水泥、石膏板这类有保质期的物资。如果业务不要求批次管理,至少也要保留一个空的批次号,方便未来扩展。
4. 核心表结构落地:采购、销售与库存流水的建表细节
4.1 采购侧三表结构:订单、入库单与结算单怎么连
采购业务在建表时要拆成三个环节:采购订单、采购入库、采购结算。三者用订单号或入库单号关联,但不能合并成一张大表。原因是“先货后票”是建材行业常态,货到了供应商发票还没开,结算金额和订单金额经常不一致,有一张独立的结算单才能应对这种时间差。
CREATE TABLE po_order ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT '采购订单号', supplier_id BIGINT UNSIGNED NOT NULL, warehouse_id BIGINT UNSIGNED NOT NULL, order_date DATE NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT '0草稿 1已审核 2部分入库 3已完成 4已作废', total_amount DECIMAL(18,4) NOT NULL DEFAULT 0, remark VARCHAR(255) NOT NULL DEFAULT '', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_supplier_date (supplier_id, order_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单主表'; CREATE TABLE po_order_detail ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, order_id BIGINT UNSIGNED NOT NULL, material_id BIGINT UNSIGNED NOT NULL, qty DECIMAL(18,4) NOT NULL, unit_price DECIMAL(18,6) NOT NULL COMMENT '采购单价', received_qty DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '累计已入库数量', PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购订单明细表'; CREATE TABLE po_inbound ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, inbound_no VARCHAR(32) NOT NULL COMMENT '入库单号', order_id BIGINT UNSIGNED NOT NULL COMMENT '关联采购订单', supplier_id BIGINT UNSIGNED NOT NULL, warehouse_id BIGINT UNSIGNED NOT NULL, inbound_date DATETIME NOT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT '1审核通过 0作废', total_qty DECIMAL(18,4) NOT NULL DEFAULT 0, total_amount DECIMAL(18,4) NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_inbound_no (inbound_no), KEY idx_order_id (order_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购入库单';这个设计里最关键的字段是po_order_detail.received_qty。每次入库审核通过后,程序要同时做三件事:更新明细表的received_qty、写库存流水、更新库存余量。三者必须在一个数据库事务里完成,否则会出现“入库单显示完成但库存没加”的问题。status = 2 部分入库的判断逻辑是received_qty < qty,业务查询“还有多少货没到”就是qty - received_qty,这个字段是高频查询条件,一定要建索引。
采购入库单为什么还要冗余supplier_id?因为后来对账时经常会只拿入库单号找供应商,不 JOIN 订单表能少一次查询。这个冗余是刻意的,不是设计失误。更新received_qty时要注意并发,不要直接用received_qty = received_qty + ?,而是先查一次再更新,或者用乐观锁。
4.2 销售侧与退货:发货、签收、退货折让怎么落库
销售侧的表结构与采购侧对称,但有一个建材行业特有的场景需要单独设计:退货并不是简单的“数量反方向冲销”。比如客户拉走 10 吨钢材,加工时发现有 0.5 吨规格不对,退回时可能只退 7 根螺纹钢,而对方仓库按根入库。所以销售退货单必须能按“原发货明细”关联,并且允许退回不同计量单位的数量。为了让对账能对上,退货单里要同时记录“原发货单号”和“原发货明细ID”,而不是只记录物料编号。
销售出库涉及财务上的“应收”逻辑。发货不等于收款,建材行业普遍存在账期,所以销售出库审核后要生成应收单,回款时写收款单并核销应收。这个核销关系也应在数据库层设计好:应收单上有invoice_no、amount、settled_amount字段,回款时更新settled_amount,已核销金额等于应收金额时状态变为已核销。
如果业务方要求折扣、折让,建议增加单独的字段而不是直接改单价。比如discount_amount记录整单优惠,deduction_amount记录质量扣款。直接在unit_price上改,会污染历史价格数据,之后分析“某型号钢材平均成交价”时就再也算不准了。
4.3 索引与唯一约束:防止单号重复和并发放大
建表时最容易忽略的是索引方向与业务查询不匹配。采购订单按“供应商 + 下单日期”查,索引就是(supplier_id, order_date);销售发货按“客户 + 发货日期”查,索引就是(customer_id, outbound_date)。但同类索引别建太多,一个表超过 5 个索引就会拖慢写入性能。建材业务每天的单据量通常不会特别大,几百张单顶天了,所以索引设计的原则是够用就好。
唯一约束则是给业务兜底的。order_no、inbound_no、outbound_no这些单据号必须建唯一索引,程序层做了幂等校验,数据库层也要挡住重复数据。常见的一个翻车场景是:业务员点了两下“保存”,程序生成了两个单号,但前台显示同一个单。解决方式就是在数据库建UNIQUE KEY uk_order_no (order_no),第二次插入直接报Duplicate entry,程序捕获这个异常提示用户“请勿重复提交”。
在数据库默认隔离级别为REPEATABLE READ的 MySQL 中,并发生成单号时如果用“先查最大号再加一”的方式,两个并发事务可能拿到同一个序号。解决方案可以用SELECT ... FOR UPDATE锁住单号序列表,或者直接用数据库的AUTO_INCREMENT生成单号主键。我实际项目里用过独立序列表的方式,稳定但稍微多一条 SQL。如果是中小型项目,也可以直接用 Redis INCR 生成当日流水号,但要注意 Redis 不可用时要能回退到数据库方案,否则单号断掉很麻烦。
5. 避坑专章:建材物资数据库设计的 5 个血泪经验
5.1 计量单位混乱导致库存对不上账
现象:水泥采购入库按“吨”记录,销售出库按“袋”,月底库存数量变成了负数,采购明细和销售明细完全对不上。原因:物料字典里只有一个默认计量单位,缺少“计量单位换算关系”,系统没有能力在吨、千克、袋之间自动转换。解决:为物料建一张单位换算表,例如 1 吨 = 20 袋(50kg/袋),换算因子存在unit_conversion表里。业务单据录入时选择实际计量单位,底层库存统一折算成“基准单位”(通常选最小单位)来存储。库存流水表里所有数量都换算成基准单位,这样对账时才能统一。
5.2 双精度浮点存储金额导致对账翻车
现象:金额对账时时不时差几分钱,明细加总对不上总额,财务提出质疑。原因:建表时为了省事把金额字段用FLOAT或DOUBLE,浮点数在二进制下无法精确表示十进制小数,累加次数多了误差就暴露了。解决:金额用DECIMAL(18,4),单价用DECIMAL(18,6),一律不允许用浮点类型。这条没有商量余地,所有和钱相关的计算都必须在数据库层用DECIMAL完成,程序语言里的float类型也不要用。另一点是除法计算单价时注意四舍五入规则,入库总金额除以数量的结果要保留 6 位小数,入账时再四舍五入到分。
5.3 直接修改库存数导致流水黑匣子
现象:盘点发现库存有差异,实施人员直接在数据库里执行UPDATE stock_balance SET qty = 95 WHERE material_id=123,一周后再盘点又差了,而且没有任何记录能解释差异来源。原因:绕过流水直接改余量表,破坏了“流水驱动余额”的不可追溯原则,后续审计谁都说不清那 5 吨去哪了。解决:坚决禁止直接 UPDATE 库存余量表。盘点差异必须通过“盘点调整单”来落账,系统自动生成一条biz_type = 50的盘点调整流水。调整单要记录盘点前的账面数量、实盘数量、差异数量、调整原因、操作人,并经审核后才能生效。数据库层不要给开发人员开通直接改库存表的权限,生产环境 DML 操作都要经过审计。
5.4 并发开单生成重复单号
现象:两个仓管员同时按下“保存入库单”,系统生成了相同单号,导致后续查询和关联单据全部串数据。原因:单据号在程序里用“当前日期 + 查库取最大流水号 + 1”的方式生成,两个并发请求读到同一个最大流水号,生成相同编号。解决:数据库层对单据号字段建唯一索引,程序层针对唯一索引冲突做重试或友好提示。如果单号要求连续且不能有断号,用“单号序列表 + 行锁”的方式,序列行上执行SELECT ... FOR UPDATE,拿到锁后自增再提交。如果允许断号,直接数据库自增 ID 就够,不建议为了展示效果用太复杂的生成器。
5.5 没有数据字典导致三个月后没人敢动表
现象:数据库建表时只写了status TINYINT、type INT,没有任何注释。三个月后需求变更,没人知道status=1是已审核还是已作废,开发只能翻程序代码,甚至靠猜。原因:建表脚本里没有COMMENT,也没有在项目里维护一份数据字典文档。解决:每张表、每个字段建表时必须写COMMENT,TINYINT类型的状态字段要在 COMMENT 里写明枚举含义。同时维护一张“数据字典表”,用元数据表来管理状态、单据类型、业务类型。数据字典本身也可以建表存进数据库,比如sys_dict_type和sys_dict_item,后台管理界面直接维护,避免每次加一个状态都要发版。这个习惯在项目初期看不到价值,项目中期以后会救你一命。
6. 用三组对账 SQL 给数据库设计做验收
6.1 采购入库与结算对账
数据库设计得再漂亮,最终要接受数据一致性检验。我做这类系统有个习惯,上线前先写三组对账 SQL,能跑平说明设计基本没问题,跑不平就一条条查。第一组是采购入库与结算:
SELECT i.inbound_no, SUM(d.qty) AS inbound_qty, SUM(d.qty * d.unit_price) AS inbound_amount, IFNULL(s.settled_amount, 0) AS settled_amount, SUM(d.qty * d.unit_price) - IFNULL(s.settled_amount, 0) AS diff FROM po_inbound i JOIN po_inbound_detail d ON d.inbound_id = i.id LEFT JOIN ( SELECT inbound_id, SUM(amount) AS settled_amount FROM supplier_settle WHERE status = 1 GROUP BY inbound_id ) s ON s.inbound_id = i.id WHERE i.status = 1 GROUP BY i.id, i.inbound_no, s.settled_amount HAVING ABS(diff) > 0.01;这条 SQL 的作用是找出“入库了但结算金额对不上”的单据。ABS(diff) > 0.01是容差控制,因为结算单可能按订单金额整单结算,不完全等于入库金额。正常的业务情形是采购订单分两批入库,供应商一次性开票,这时结算金额等于两批入库之和,而不是等于单批入库金额。所以这条 SQL 按入库单维度跑出差异后,还需要按订单维度再核对一次,不能只依赖单条结果。
6.2 库存流水与库存余额对账
第二组是库存流水与余量表的一致性检查,这是整个系统最重要的校验:
SELECT t.warehouse_id, t.material_id, t.batch_no, SUM(t.qty_in) - SUM(t.qty_out) AS trans_qty, b.qty AS balance_qty, SUM(t.qty_in) - SUM(t.qty_out) - b.qty AS diff, m.material_code, m.brand, m.specification FROM stock_transaction t JOIN material_dict m ON m.id = t.material_id LEFT JOIN stock_balance b ON b.warehouse_id = t.warehouse_id AND b.material_id = t.material_id AND b.batch_no = t.batch_no GROUP BY t.warehouse_id, t.material_id, t.batch_no, b.qty, m.material_code, m.brand, m.specification HAVING ABS(diff) > 0.0001;跑出任何一行结果,都说明存在“流水有记录但余额没更新”或“余额被手动改过”的问题。在实际项目里,这种查询要放在每天凌晨的定时任务里跑,有异常就推送到运维群。除了数量对账,成本金额对账也要做,用流水重新计算移动加权平均成本,和stock_balance.total_cost对比,差异超过 0.01 就要查。成本对账的 SQL 比数量对账复杂,因为要按时间顺序重放流水计算移动加权,不能简单用SUM(qty_in * unit_cost)。常见做法是写一个存储过程或 Python 脚本,循环读出某个物料的全部流水,模拟计算每笔变动后的结存成本,再与余量表比对。
6.3 出库可用量校验与冻结机制
最后一个进阶技巧是可用量控制。很多建材系统会把订单和出库分离:下销售订单时冻结库存,出库时扣减冻结量,取消订单时释放冻结量。对应地,库存余量表要增加frozen_qty字段,实际的可用数量是qty - frozen_qty。写销售订单时插入一条冻结流水,出库审核时生成出库流水并同时释放冻结。这个机制能有效防止“一货多卖”——两个业务员同时给客户下单,最后仓库只有一份货。实现上可以用一条UPDATE stock_balance SET frozen_qty = frozen_qty + ? WHERE warehouse_id=? AND material_id=? AND batch_no=? AND (qty - frozen_qty) >= ?来判断是否够冻,条件不满足说明可用量不足,直接回滚事务,省得查询后再判断产生并发窗口。
写到这里,想起早年做一个项目时,就是在库存余量表上偷懒省了批次字段,结果客户半年后要求按质保书追溯批次,我花了两个通宵补数据清洗,那种滋味记忆犹新。现在做这类系统,我宁愿在数据库设计上多抠一天,也不愿意上线后给业务方当救火队员。每张单据、每个字段、每条流水都先问一句“三个月后还能看懂吗、对得上账吗”,这样交付出去的系统才敢让人长期用。希望帮到你。
本文还有配套的精品资源,点击获取