1

我正在构建一个在线发票平台,但我正在为规范化的概念而苦苦挣扎,因为它与放置在每张发票上的行项目有关。我的表结构如下:

雇主

  • ID(PK,自动增量)
  • empID (int)
  • 名称(varchar)
  • (其他无关字段)

成员

  • ID(PK,自动增量)
  • empID(对雇主的 FK)
  • 会员编号(整数)
  • 名称(varchar)
  • (其他无关字段)

计划

  • 平面(PK,自动增量)
  • 描述(varchar)
  • 价格(十进制)

member_plans

  • ID(PK,自动增量)
  • memberid (FK 到会员)
  • planid(FK 到计划,int)
  • 状态(int,1 表示活动,0 表示非活动)
  • 开始(日期时间)
  • 结束(日期时间,可以为空)

发票

  • invoiceid(PK,自动增量)
  • empID(对雇主的 FK)
  • 日期(日期时间)
  • 小计(十进制)
  • 增值税(十进制)
  • previous_owed(十进制)
  • 状态(整数)

发票项目

  • ID(PK,自动增量)
  • invoiceid(FK 到发票)
  • memberid(FK 到成员)
  • planid(FK 到计划)
  • 价格(十进制)

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

我希望能够随时返回并根据所选日期生成发票。目前,我基本上每个月都会导入所有现有 member_plans 的快照,导入 invoice_items 并分配一个 invoiceid。这似乎有很多数据被复制(尽管可能需要一个真正的快照,因为 member_plans 中的开始和结束日期并不能真正指示某些因素,即结束日期可以在任何时间点更改“好像“由于退款或法律问题,会员在特定时间段内从未活跃过)。

是否有适当/有效的方法来规范 invoice_items 表,或者我列出的方法真的是唯一正确的方法吗?无论如何,计划是在 7 个月后归档发票,因此虽然表大小值得关注,但它并不是一个永无止境的增长表。

4

1 回答 1

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)
);

您在此表中提到了“描述”,但将其排除在列列表之外。我也漏掉了。

检查 MySQL 中的约束

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

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

其他一些东西要看。. .

发票 ID 号通常不是自动递增的整数。自动递增整数可以有间隙。会计师讨厌差距。迟早,他们会想知道发票编号 10156 发生了什么,并且他们喜欢听到“这可能只是由于交易失败或其他原因导致 dbms 丢弃的数字”。

如果employees.empID 是唯一的,添加另一个带有ID 号的列并不会使其更加唯一。members.memberid 也是如此。请参阅货物崇拜编程

于 2012-07-26T11:19:45.350 回答