我应该规范发票数据库表集吗?

时间:2012-07-19 15:59:28

标签: mysql database database-design

我正在建立一个在线发票平台,但我正在努力实现规范化的概念,因为它与每张发票上的订单项有关。我的表格结构如下:

雇主

  • ID(PK,autoincrement)
  • empID(int)
  • name(varchar)
  • (其他不相关的字段)

构件

  • ID(PK,autoincrement)
  • empID(FK给雇主)
  • memberid(int)
  • name(varchar)
  • (其他不相关的字段)

计划

  • planid(PK,autoincrement)
  • description(varchar)
  • 价格(小数)

member_plans

  • ID(PK,autoincrement)
  • memberid(FK to member)
  • planid(FK to plans,int)
  • status(int,1表示活动,0表示非活动状态)
  • start(datetime)
  • end(datetime,可以为null)

发票

  • invoiceid(PK,autoincrement)
  • empID(FK给雇主)
  • 日期(日期时间)
  • 小计(十进制)
  • 增值税(小数)
  • previous_owed(十进制)
  • status(int)

invoice_items

  • ID(PK,autoincrement)
  • invoiceid(FK到发票)
  • memberid(FK to members)
  • planid(FK to plans)
  • 价格(小数)

会员属于雇主团体。雇主代表其成员支付会费。每个成员可以选择多个计划,因此发票将包含行项目(memberid,name)以及每个成员的子项目(planid,description,price)。每个成员的所有计划的总和被组合在一起并开具发票给雇主进行在线审查。

我希望在任何时候都可以根据所选日期返回并生成发票。目前,我将每月导入所有现有member_plans的快照,导入invoice_items并分配invoiceid。这似乎是很多数据被复制(虽然它可能是真正的快照所必需的,因为member_plans中的开始和结束日期并不真正指示某些因素,即结束日期可以在任何时间点更改“就好像“由于退款或法律问题,该成员在某段时间内从未处于活动状态。”

是否有正确/有效的方法来规范化invoice_items表,或者我列出的方法真的是唯一正确的方法吗?计划是在7个月之后存档发票,因此在关注表格大小时,它不是永无止境的增长表。

1 个答案:

答案 0 :(得分:1)

以下是invoice_items的一些小设计问题。

ID (PK, autoincrement)
invoiceid (FK to invoices)
memberid (FK to members)
planid (FK to plans)
price (decimal)

ID是不必要的,没有任何其他更改,是一个损坏的代理键。它被打破了,因为它作为代理人应该取代自然键,但你还没有创造出一把自然的钥匙。 (单词代理基本上意味着"代替"。例如,代理母亲代替自然分娩母亲。)

让我们看看一些伪SQL。

create table invoice_items (
  invoiceid integer not null,

  -- Traditionally, line items are numbered sequentially starting at 1
  -- for each invoice. Your traditions might be different. "Some sane value"
  -- prevents you from sending someone a 10,000 line invoice by mistake.
  -- See below (way below) for implementing CHECK() constraints in MySQL.
  -- A constraint that says, "Start with 1, no gaps" would be nice. I'll
  -- let you code that one. ;)
  line_item_num integer not null check (line_item_num >= 1 and
                                        line_item_num <= [some sane value]),

  memberid integer not null,
  planid integer not null,

  -- Choose the range to fit your application, and to prevent egregious mistakes.
  price decimal(...) not null check (price between -10000 and 10000),

  -- This constraint implements the traditional idea of invoice line items.
  primary key (invoiceid, line_item_num),

  -- This constraint prevents billing a single member plan twice on one invoice.
  -- It might need to be dropped. For example, if you invoice one line item for
  -- the base price for a member plan, then also invoice one line item for 
  -- a discount to the same member plan, you'd need to drop this constraint.
  unique (invoiceid, memberid, planid),

  foreign key (invoiceid) references invoices (invoiceid),

  -- This foreign key needs to reference the single table member_plans, not the 
  -- two tables members and plans. Referencing the single table prevents you from
  -- invoicing a member for a plan that member hasn't chosen.
  foreign key (memberid, planid) references member_plans (memberid, planid)
);

你提到&#34;描述&#34;作为此表的一部分,但将其从列列表中删除。我也把它留了出来。

检查MySQL中的约束

MySQL不支持CHECK()约束。在某些情况下,将CHECK()约束实现为另一个表的外键引用是切实可行的。例如,实现我为上面的line_item_num编写的CHECK()约束作为对行项目编号表的外键引用是切实可行的。

在其他情况下,外键引用可能不实用。例如,价格可能有太大的范围,您无法实现这种方式。 -10000.00到+10000.00的范围需要几百万行。一种替代方法是使用触发器。在最坏的情况下,您可能不得不依赖应用程序代码和异常报告。 (偶尔会运行一个异常报告来搜索已经滑过裂缝的无效值。)

要看的其他一些事情。 。 。

发票ID号通常不是自动递增整数。自动增量整数可能存在差距。会计师讨厌缺口。迟早,他们想知道发票编号10156发生了什么,他们就像听到&#34;这可能只是dbms丢弃的一个数字,因为失败的交易或某事。&#34;

如果所有employees.empID是唯一的,则添加另一个带有ID号的列并不会使其更加独特。 member.memberid也是如此。请参阅cargo cult programming