库存数据库设计


77

这不是一个关于“编程”(不是特定于任何语言或数据库)的问题,而是更多的设计和体系结构问题。这也是“什么是做X的最佳方法”类型的问题。我希望不会引起太多“宗教”争议。

过去,我开发的系统以一种或另一种方式保留某种形式的物品清单(与什么物品无关)。一些使用不支持事务的语言/数据库。在那些情况下,我选择不将现有的项目数量保存在项目记录的字段中。相反,库存量计算共计收到库存-总库存的销售。由于软件,这几乎没有导致存货差异。该表已正确索引,并且性能良好。有一个存档过程,以防记录量开始影响性能。

现在,几年前,我开始在这家公司工作,并且继承了一个跟踪库存的系统。但是数量保存在字段中。注册条目后,收到的数量将添加到物料的数量字段。出售物品时,数量会减去。这导致了差异。我认为这不是正确的方法,但是以前的程序员对此发誓。

我想知道在设计这种系统的正确方法上是否存在共识。此外,还有哪些可用资源(印刷或在线)来寻求指导。

谢谢


15
当您说“这里的以前的程序员都发誓”时,您是说他们每次都要进行这项工作时都发誓吗?
MusiGenesis

Answers:


52

我在目前的公司中见过两种方法,并且肯定会倾向于第一种方法(根据股票交易计算总数)。

如果仅在某个字段中存储总数量,则不知道如何得出该数量。没有交易记录,您最终可能会遇到问题。

我编写的最后一个系统通过将每笔交易存储为具有正数或负数的记录来跟踪库存。我发现它运作良好。


1
+1我遇到了同样的难题,我现在认为这是最好的选择
INS 2010年

4
@Neil-您能否评论这种方法的性能。这似乎是一种首选方法,但是如果您有数百种产品和数千笔交易,则如何或何时计算累计总数。还是只是在知道可以根据需要重新计算时就放心地将累计总数存储在另一个位置?
Simon_Weaver

11
我进行了一项测试-我发布了2000多种产品的大约300万条记录(正面和负面的库存调整)。用SKU对所有行的总和进行计数只花了不到一秒钟的时间。我必须说我对它的运作速度如此之快感到非常震惊,对于我正在做的项目,我肯定还有价值几个数量级的增长,并且不需要很快担心。显然,如果您要在网站上显示实时库存总计,则可能要缓存它们,但是即使在所有2000种产品中,我也几乎可以立即计算出总计。
Simon_Weaver

2
银行/会计系统与交易(借记或贷记)的功能相似,通常作为单独的字段,但具有相同的求和效果。主要是出于操作原因,创建了每月余额(通常有一个验证月末流程),但这也是一种能够累加12个每月增量数量以便快速累加一年而不是一年中的每笔交易的方法。经过数年和数百万个帐户的处理后,性能收益是实实在在的,而且几个月前还可以进行更正,只需要重新计算该月的收盘价即可。
2014年

1
您应同时应用两个字段:1个字段为数量,1个表格进行交易。数量字段可以在需要时重新计算。您需要此字段是因为性能。当月底。我们应该将所有数字从前一个月移到新月。这是我的ERP系统。运作良好
Gray Wolf


9

这取决于库存系统的意义远不仅仅在于盘点物品。例如,出于会计目的,您可能需要基于FIFO(先进先出)模型来了解库存的会计价值。无法通过简单的“已收到的库存总数-已售出的库存总数”公式来计算。但是他们的模型可能很容易计算出这一点,因为他们可以随时修改会计价值。我不想详细介绍,因为这不是编程问题,但是如果他们发誓,也许您不完全了解他们必须满足的所有要求。


7

两者都有效,具体取决于情况。当满足以下条件时,前者是最好的:

  • 要累加的项目数量相对较少
  • 很少或没有例外情况需要考虑(退货,调整等)
  • 库存物品数量不是经常需要的

另一方面,如果您有大量的物品,几种特殊情况并且经常访问,则保持物品数量的效率将更高。

还要注意的是,如果你的系统有差异,然后它有缺陷,应追踪并消除

我已经完成了两种方法的系统,并且两种方法都可以正常工作-只要您不忽略这些错误!


1
嗯。回报真的不是例外吗?除非您销售的是纯易腐烂的物品或其他无法转售的物品
Simon_Weaver 2011年

2
@Simon:我写的早期库存系统之一是一家定制冰淇淋店。退货不仅是例外,而且几乎是不可能的;-)
史蒂芬·A·洛

5

看一下ARTS(零售技术标准协会)数据模型(http://nrf-arts.org)。它使用一个StockLedger表,其中记录了每个项目,并且库存的更改都在InventoryJournalEntries中进行了跟踪。


4

重要的是要考虑现有系统以及更改它的成本和风险。我使用的数据库可以像您一样存储库存,但是它包含审计周期并像收据一样存储调整。它似乎工作得很好,但是每个参与人员都受过良好的培训,并且仓库工作人员学习新程序的速度并不很快。

在您的情况下,如果您希望在不更改整个数据库结构的情况下进行更多跟踪,那么我建议您添加一个跟踪表(类似于“事务”解决方案中的表),然后将更改记录到库存级别。将大多数更改更新到库存级别应该不难,这样它们也可以保留交易记录。您还可以添加一个定期任务,每隔几个小时左右将库存级别备份到事务表中,这样,即使您错过了事务,您也可以发现更改发生的时间或还原到以前的状态。

如果您想了解大型应用程序是如何工作的,请查看SugarCRM,它们具有库存管理模块,尽管我不确定它如何存储数据。


4

我认为这实际上是一个通用的最佳实践问题,即每次需要总计时都要进行(相对)昂贵的计数,而每次更改时都要进行计数,然后将计数存储在字段中,并在需要时读取该字段总。

如果我不能使用交易,那么每次需要总计时我都会进行实时计数。如果可以进行交易,则可以安全地执行库存更新操作并在同一笔交易中保存重新计算的总计,这将确保计数的准确性(尽管我不确定这是否适用于多个用户)点击数据库)。

但是,如果性能并不是真正的大问题(现代数据库足以计数行,而我什至很少担心这一点),我每次都会坚持使用实时计数。


3

我会选择第一种方式

计算出的现有数量是收到的库存总数-已售出的库存总数

正确的方法,IMO。

编辑:我也想将任何库存损失/损坏纳入系统,但我确定您已经涵盖了这一点。


2

我曾经在解决此问题的系统上工作过。我认为理想的解决方案是预先计算的色谱柱,这可以使您两全其美。您的总数将是某个地方的字段,因此无需进行昂贵的查找,但它不会与其余数据不同步(数据库保持完整性)。我不记得哪个RDMS支持预计算列,但是如果您没有事务,则可能也不可用。

您可能会使用触发器来伪造预计算列(非常有效……我认为没有缺点)。不过,您可能需要交易。恕我直言,在执行这种受控的非规范化时,保持数据完整性是触发器的唯一合法用途。


2

Django库存更适合固定资产,但可能会给您一些想法。

IE:ItemTemplate(类)-> ItemsOnHand(实例)

ItemsOnHand可以链接到更多ItemTemplates。需要打印机和墨盒示例。这还允许为每个ItemOnHand设置重新排序点。

每个ItemsOnHand都链接到InventoryTransactions,这使审核变得容易。为了避免计算数千笔交易中的实际手头物品,使用了仅是余额+日期的检查点。要计算现有商品,请查询以找到最新的检查点,然后开始添加或减去商品以找到商品的当前余额。定期定义新的检查点。


1

我可以看到使用两列会有所好处,但是我没有关注有关差异的部分-您似乎在暗示拥有两列(输入和输出)比单列(当前)更不容易出现差异。这是为什么?


0

没有一两列,我所说的“已收到的库存总数-已售出的库存总数”是这样的:

Select sum(quantity) as inventory_received from Inventory_entry
Select sum(quantity) as inventory_sold from Sales_items

然后

Qunatity_on_hand = inventory_received - inventory_sold

请记住,我过分简化了我的初始解释。我知道库存中还有很多事情可以跟踪数量,但是在这种情况下,问题就出在我们要解决的问题上。此时更改的原因恰恰是支持当前设计所引起的问题的成本。

我还想提到,尽管这不是一个“编码”问题,但与算法和设计有关,恕我直言是非常重要的主题。

到目前为止,感谢大家的回答。

纳尔逊·马莫尔


2
这种特定解决方案的缺点是,随着时间的流逝,性能会越来越差,因为您必须无限期地记录所有收到或出售的库存,才能获得正确的当前数量!
史蒂文·A·洛

0

我们解决了不同的问题,但是您可能会对某些问题感兴趣。

我们允许系统做出“最佳猜测”,并定期向用户提供有关任何看起来错误的猜测的反馈。

要将其应用于库存,您可以具有3个字段:

inventory_received
inventory_sold
estimated_on_hand

然后,您可以按照以下方式运行一个进程(每天吗?):

SELECT * 
FROM   Inventory
WHERE  estimated_on_hand != inventory_received - inventory_sold

当然,这取决于用户查看此警报并对其进行处理。

另外,您还可以通过更新stock_sold / received或添加另一个字段“ inventory_adjustment”(可能为正数或为负数)来以某种方式重置库存。

...只是一些想法。希望对您有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.