企业常见需求-仓库模型讨论

360影视 国产动漫 2025-06-03 09:22 4

摘要:最近一直在做有关仓库管理方面的项目,想把相关概念捋一捋,同时设计个简易的仓库模型来讨论一下所面临的挑战及解决方案。

最近一直在做有关仓库管理方面的项目,想把相关概念捋一捋,同时设计个简易的仓库模型来讨论一下所面临的挑战及解决方案。

企业A是一家制造型企业,他们拥有一间大型的仓库,存放所有用于生产的原材料,原材料都是整箱包装的。他们利用SAP系统中的仓库管理模块对仓库进行管理。

但企业A具有多个生产线,每条生产线旁边都具有一个小型的仓库(线边仓),但现在线边仓的入库,出库(领料)并未有任何系统进行管理。为了进一步精细化管理,企业A决定开发一套系统对线边仓进行管理。

线边仓往往较小,类似一个10-20平方小房间里面有多个小型货架,每个货架有多层,每层有多个隔间用于存放生产所需的原材料。

线边仓的原材料都是从大仓库中整箱拆箱而来,所以线边仓的最小存储粒度是原材料的编码和数量。

根据需求,显然我们可以得出存储位置为一个实体。原材料是另一个实体。某个货架位置上有多个入库和出库的事件。根据这个简单的概念我们可以设计出数据模型如下:

Locations:表示货架的位置。

Products:表示原材料。

Location_Events:表示某个货架位置上的入库、出库事件。(从数据库建模角度看是一个多对多关系)

入库事件:实际生产中,入库可能来自于总仓,也可能是由产线退回,也可能是其他仓库调拨。所以为了对这个需求进行建模我们单独设置了EventType字段进行记录。

出库事件:同上,出库也包括一般正常领料出库,也包括调拨出库,和退回出库。这个需求也可以用EventType字段进行表示。

Qty:入库数量,出库数量。入库记为正数,出库记为负数。

解决方案1:直接根据Location_Events中的记录按位置和产品编码进行分组,汇总Quantity字段即可得到每个产品在每个位置上的库存数量。

select LocationCode,p.ProductCode,SUM(Quantity)as Quantity from Location_Events as e

innerjoin Products as p on p.ProductCode=e.ProductCodegroupby LocationCode,p.ProductCode

这个方案简单直接,但是每次查看库存都执行一次查询显然在性能上有些浪费。所以可以采用方案2.

解决方案2:使用物化视图,把上面的查询变成一个物化视图,在SQLServer中体现为索引视图。索引视图其实就是将视图变成一个带有聚集索引的实体表,当对基础表进行INSERT,UPDATE,DELETE操作时会自动更新索引视图。

--Set the options to support indexed views.

SET NUMERIC_ROUNDABORT OFF;SET ANSI_PADDING, ANSI_WARNINGS, CONCAT__YIELDS_, ARITHABORT, QUOTED_IDENTIFIER, ANSI_S ON;--Createviewwith SCHEMABINDING.IF OBJECT_ID('dbo.Inventory''view')ISNOT DROPVIEW dbo.Inventory;GOCREATEVIEW dbo.Inventory WITH SCHEMABINDINGASselect LocationCode,p.ProductCode,SUM(Quantity)as Quantity,COUNT_BIG(*)asCountfrom dbo.Location_Eventsas einnerjoin dbo.Productsas p on p.ProductCode=e.ProductCodegroupby LocationCode,p.ProductCode,p.ProductNameGO--Create an indexon the view.CREATEUNIQUE CLUSTERED INDEX IDX_V1 ON dbo.Inventory( LocationCode, ProductCode);GO

解决方案3:物化视图手动实现。创建一个实体表(包含LocationCode、ProductCode、Quantity),在Location_Events表中创建一个触发器,当表中数据新增时,触发器捕获新增的数据,如果是入库操作则根据LocationCode和ProductCode作为唯一键去更新Quantity数量。(这里又存在上篇写过的一个问题,如果存在则更新,如果不存在则插入。)

性能问题:以上三种方案大并发情形下都存在性能问题。尤其是在SQLServer,MySQL这种默认为提交读事务隔离级别下会存在读写相互阻塞的情况。解决方案是修改数据库的默认隔离级别为行版本控制,SQLServer为READ_COMMITTED_SNAPSHOT。MySQL为MVCC。

如果库存的实时性要要求不高,那完全可以采用定时更新物化视图的方案,例如每天夜间进行库存的刷新。

其实库存粒度就是库位上存放的最小库存单位。

对于线边仓主要是存放散装原材料,例如:一个零件,一个刀具,一盒锡球。

对于大仓库主要是存放整包原材料,例如:一包100个单位的零件,一包10个单位的刀具,一包2000个单位的锡球。

例如:供应商A生产的切割刀型号为N001,批次为20250102,12个一包,那么这把刀具的库存唯一编码为:A-N001-20250102-12。型号、批次、包装规格的不同都会形成不同的编码。这个编码为库存而存在。

仓库类型

存放货品类型

编码样例

总仓(Warehouse)

原材料整包存放

A-N001-20250102-12

散件存放

其实零售业SKU的概念很好解释了这个编码。

这里引用亚马逊对SKU的解释

SKU 是库存单位 (stock-keeping unit) 的缩写,代表零售商库存中的一种商品。SKU 编码是一个包含字母和数字的唯一标识符,用于说明商品的重要特征,例如品牌、颜色和尺寸。这些编码由零售商生成,每个企业的 SKU 都是独一无二的。这意味着企业的每种商品都应该有一个不同的编码,并且不同零售商对同一商品的编码应该是不同的。

供应商负责生成,供应商将原材料打包好,每个包装上按照材料类型,型号,批号,包装规格生成一个唯一码贴在外包装上。但有时候供应商的唯一码不能满足企业的要求。

企业自己生成,企业自己维护各种原材料类型,并将供应商提供的整箱包装进行编码。这种编码更符合企业要求,但必须有专人进行维护。(SAP系统中都有编码工具可以帮助生成这类编码)

企业线边仓往往都是零散入库和出库,企业往往直接使用供应商的编码作为最小库存编码。

最终的编码表可能类似下面这样

扩展讨论

有时候货架上存放的东西并不是原材料,而是半成品。无论货架上存放的是什么,总归有一个编码对应到货架编码上。这个编码可以是企业业务使用的例如Lot编码之类,也可以是成品仓中最后的发货所用的编码。

库位和物品是多对多的关系。入库和出库是这种关系的体现。无论入库出库业务要求多么复杂,都是不同事件而已。

最小库存单位是非常重要的,要准确的定义库存编码。(例如:由材料类型、型号、批次、生产商、包装规格来确定)

并发小则采用最简单的物化视图方案,实现简单查询效率高。

并发大则要考虑启用数据库行版本特性或延迟更新库存。

来源:opendotnet

相关推荐