建模数据 - 发票和行项目

时间:2014-05-27 04:39:37

标签: django database-design data-modeling

我以Django作为后端创建基于Web的销售点(思考收银机)解决方案。我一直都是经典的'建模发票及其订单项的方法。

InvoiceTable
  id
  date
  customer
  salesperson
  discount
  shipping
  subtotal
  tax
  grand_total
  [...]

InvoiceLineItems
  invoice_id // foreign key
  product_id
  unit_price
  qty
  item_discount
  extended_price
  [...]

在尝试研究最佳实践后,我发现没有多少 - 至少没有广泛使用的权威来源。

Kimball Group建议:"我们建议您将标题的所有维度降低到订单项。"""而不是保留交易标题“对象”的操作概念。"

请参阅http://www.kimballgroup.com/2007/10/02/design-tip-95-patterns-to-avoid-when-modeling-headerline-item-transactions/http://www.kimballgroup.com/2001/07/01/design-tip-25-designing-dimensional-models-for-parent-child-applications/

我刚接触开发(之前只使用过桌面数据库软件) - 但据我所知,这是有道理的,因为我们可以按照我们想要的方式深入查看数据(尽管我想我们可以做到与加入表格的第一种方法相同。

我的问题

  • 每行需要重复发票ID(因此我们可以生成发票总计等数据)。这是这种数据建模方式的有意特征吗?
  • 我们经常有发票级数据,如票据,折扣,运费等。 - 我们如何使用此方法表示这些数据?有些折扣是针对特定产品的 - 所以它们都属于订单项,其他则是发票范围广(想想你购买两个独立产品并获得两个折扣的交易) - 我们可以以某种方式在订单项中分配它吗?与运费相同,通过在订单项之间进行分配来进行分配?
  • 我们如何处理发票'备注' - 我们有打印和/或内部注释,我们是否会将数据放入订单项中,并为每个订单项重复一次?这似乎违反了数据规范化。把它放在相关的表格中?

    任何使用此方法的开源项目我都可以查看一下?不知道如何搜索它们。

2 个答案:

答案 0 :(得分:3)

听起来你混淆了关系设计和维度设计。

关系设计用于促进事务处理,并最小化数据异常和重复。它是您的运营数据库。尺寸设计用于促进分析。

关系设计将有一个发票表和一个line_items表,而维度设计将有一个company_invoices_customer事实表,其中包含一粒发票行项目。

由于这是针对POS的,我假设您首先想要一个关系设计。

关于你的问题:

首先,这种情况有大量良好的数据建模模式。见https://dba.stackexchange.com/questions/12991/ready-to-use-database-models-example/23831#23831

  

每行需要重复发票ID(所以我们可以   生成发票总计等数据。这是故意的吗?   这种数据建模方式的特点是什么?

  

我们经常有发票级数据,如票据,折扣,运费   收费等 - 我们如何使用这种方法来表示这些?

可能最容易/最简单的有"笔记"发票表上的字段。

对于费用和折扣,您应该使用抽象(请参阅表继承),并将它们添加为订单调整。请参阅上面链接中的Silverston一书。

  

某些折扣是特定于产品的 - 因此它们属于订单项   无论如何,其他人都是发票宽(想想你买两个的交易   分开产品并获得两者的折扣) - 我们可以   以某种方式在订单项中分配它?

项目的价格应根据其默认价格以及当前"场景"中适用的任何折扣或费用在运行时计算,附近的政府折扣示例销售日。您可以拥有相互引用的分层行项目,以保持秩序井然有序。再次,请参阅Silverston的书。

  

我们如何处理发票'备注' - 我们有印刷和/或内部   注意,我们会将数据放在订单项中,然后重复一下   每个订单项?

如果您需要订单项备注,请在订单项表格中添加备注列。

  

这似乎与数据规范化无关。把它放在一个相关的   表

如果注释可以为空,并且您希望对规范化严格,那么是,添加invoice_notes表。

答案 1 :(得分:0)

Invoice / InvoiceLineItem是父(Invoice)/ Child(Item)关系模型的示例,在标准化关系数据库中很常见。看起来你正在为IMO建立正确的关系。

  

每行需要重复发票ID(因此我们可以生成发票总计等数据)。这是这种数据建模方式的有意特征吗?

是的,这很好 - 您需要不断加入InvoiceID并按其过滤(在LineItem上,InvoiceId应该是Invoice的外键,而在Invoice上它将会可能是主键,或至少是一个独特的约束)

  

我们经常有发票级数据,如票据,折扣,运费等。 - 我们如何使用此方法表示这些数据?一些折扣是特定的产品(我假定行) - 因此它们无论如何都属于订单项,其他折扣是发票范围(想想您购买两个单独产品并获得折扣的交易)在这两个) - 我们可以以某种方式分配线项目?与运费相同,通过在订单项之间进行分配来进行分配?

经常有'似乎这些数据是可选的,每个发票或行项目可以有多个备注/折扣等。这些关系通常被建模为单独的表,外键返回到关联的发票或行项目。这样,您可以有许多笔记,许多折扣等。您可能希望在这些表上引入唯一约束,以防止多个发票具有2个相同折扣。这里的一个设计选项是行折扣和发票折扣之间是否有足够的通用性,以保证单个表对父/子进行建模,并使用可选的外键。一般来说,我会错误地分离专门的InvoiceDiscount和LineDiscount表。

  

我们如何处理发票'备注' - 我们有打印和/或内部注释,我们是否会将数据放入订单项中,并为每个订单项重复一次?这似乎违反了数据规范化。把它放在相关的表格中?

听起来这些音符可能是“静态数据”,例如一套50条标准评论,可能编纂成文。笔记'模板'应该建模为(例如NoteTemplate)。然后,您可以在LineItemNoteTemplate之间添加N:N联结表。如果这里允许有可选的自由流量注释,这可以作为联结表上的附加列(虽然有些ORMs不赞成这样做。)

  

关注

FWIW,我已经交付了几个零售系统,所有系统(现成和定制)都使用这种方法来模拟发票(以及采购订单,交货单等)。

您可能习惯使用NoSql或单个文档/聚合根存储方法,并且对标准化可能导致的大量表有所保留?虽然毫无疑问你可以为#34; Invoice"聚合根,问题将来自查询数据(例如find me the sum of all sales of XYZ ToothPaste for sales on DD/MM/YYYY,尤其是在行项目或笔记等子节点上)。

  

实施

出于性能原因,通常会汇总父发票级别上所有子订单项的净额,税金和总额的总和。虽然这可能是多余的,但它确实具有优于不断重新获得该数据的性能优势,例如,来自View或Computed列。另一个性能考虑因素是,每次提取发票时,您都会经常检索所有订单项。因此,在Parent Child数据建模中通常使用父InvoiceId作为子表上的Clustering Key(NB这与子项上的Primary Key不同,但您可以选择这里加倍,例如,如果一个行项目每个发票只能有一个产品,那么InvoiceId, ProductId可能是群集密钥和主密钥的好选择。

HTH

修改

SqlServerMySql的聚簇索引与数据的物理存储相关 - 聚类键(和填充因子)决定了记录如何存储在数据页面上。 Oracle有一个名为Indexed Organized Tables (IOT)的类似概念。

如果是亲子关系(例如InvoiceInvoiceLineItem),那么检索给定父级Invoice的所有子级是很常见的,确保尽可能多的孩子尽可能存储在同一物理页面上(如果需要多个页面,则在相邻页面上)。这将最大限度地减少读取IO。

我已经提出了一个SqlServer SqlFiddle here,它希望通过父发票说明两种聚类方法 - 一种是具有代理非群集PK的详细信息,另一种是使用复合PK到InvoiceID, ProductID