首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不同表上多个主键的外键

不同表上多个主键的外键
EN

Stack Overflow用户
提问于 2018-04-25 21:28:53
回答 1查看 201关注 0票数 0

我有一个内置了成熟的会计系统的物业管理应用程序。我有一个日记帐分录表,用于控制各种会计活动的所有过帐,例如:

发票

付款

账单

存款

在某些情况下,有必要将这些实体连接到日记账分录表格中,以便按不同的属性和单位聚合会计分录。

我正在寻找做这件事的最好方法。我有几个选择:

1)在日志条目表中添加一个外键以链接到invoice_id、payment_id、bill_id、deposit_id,但是这些外键的大多数组合将是互斥的(即存款不会有付款),因此对于给定的日记条目,我可能会在不适用于该给定的日记条目的那些外键中具有空值。

2)我可以创建一个外键,我们称它为doc_id,另一个列doc_type表示文档的类型(发票、付款、账单、存款等),并让doc_id和document_type_id的组合引用其中一个扩展表上的主键(即doc_id =1& doc_type = Invoice,该组合将引用发票表上的主键)。

到底是哪种方式更好,还是我的想法完全错了?

EN

回答 1

Stack Overflow用户

发布于 2018-04-27 01:19:07

这听起来像是一个标准的基本实体/子实体模式。有一个表,我们称它为JournalEntries,它包含所有日志条目共有的属性: ID、条目类型、创建时间、创建者等等。

代码语言:javascript
复制
create table JournalEntries(
    ID       Int  auto_generating primary key,
    EType    char( 1 )  not null check( EType in( 'I', 'P', 'B', 'D' )) -- Invoice, Payment, etc.
    Amount   currency  not null,
    CreateDate  Date not null,
    ...,     -- other common attributes
    constraint UQ_JournalEntryType unique( ID, EType ) -- create anchor for FKs
);

请注意,ID是主键,因此是唯一的。因此,从域定义的角度来看,使ID和EType组合唯一的约束是多余的。它所做的一切就是为外键定义一个锚点。

这些FK将出现在子实体表中--每个子实体对应一个表:发票、付款、账单和存款。请注意,如果在JournalEntries表中将条目定义为存款(EType = 'D'),则只能在存款表中创建相应的条目。例如,您不能在Payments表中错误地使用该ID。

让我们定义一个子实体表:

代码语言:javascript
复制
create table Invoices(
    ID    int   primary key, -- value generated by JournalEntries table
    IType char( 1 ) not null check( IType = 'I' ), -- Nothing but invoices
    ...,   -- Invoice-specific attributes
    constraint FK_InvoiceToEntry foreign key( ID, IType )
      references JournalEntries( ID, EType )
);

现在,让我们创建一个活动,该活动始终有一个与之关联的发票,并且可以有任意数量的其他条目。这些约束确保只能插入发票,并且ID值必须与定义为发票的JournalEntries条目匹配。

代码语言:javascript
复制
create table Activities(
  ID   int   auto_generating primary key,
  InvID int  not null,
  IType char( 1 ) check( IType = 'I' ),
  ...,   -- other data
  constraint FK_ActivityInvoice foreign key( InvID, Type )
);

可能有任意数量的附加条目,并且它们可能是任何条目类型,因此您需要一个交叉表:

代码语言:javascript
复制
create table ActivityEntries(
    ActID  int  not null,
    EntID  int  not null,
    DateEntered date not null,
    constraint FK_ActEntry_Activity foreign key( ActID )
      references Activities( ID ),
    constraint FK_ActEntry_JEntry foreign key( EntID )
      references JournalEntries( ID )
);

请注意,“日志条目”是与来自某个子实体表的相关数据连接的JournalEntries数据。因此,对任何日志条目的FK引用都应该引用JournalEntries表,而不是任何子实体表,即使您知道它是哪种类型的条目。因此,Activities行使用EType字段引用JournalEntries表作为额外的数据完整性工作,因为它必须是发票。交叉表包含任何类型的条目,因此它的FK目标就是PK。

注意:为了便于说明,JournalEntries表中的类型指示符由check语句约束。在实际的数据库中,一个更好的设计应该是一个条目类型查找表。这维护了数据的完整性,但是一个更灵活的设计。(再加上MySQL仍然(!)不实现检查约束。)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50023642

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档