不同表上多个主键的外键

p8ekf7hl  于 2021-06-24  发布在  Mysql
关注(0)|答案(1)|浏览(420)

我有一个完整的会计系统内置物业管理应用程序。我有一个日记账分录表,控制各种会计活动的所有过账,例如:
发票
付款
账单
存款
在某些情况下,有必要将这些实体连接到日记账分录表,以便按不同的属性和单位聚合会计分录。
我在找最好的办法。我有几个选择:
1) 在日记账分录表中添加外键以链接到发票id、付款id、账单id、存款id,然而,这些组合的大多数将是互斥的(即存款不会有付款),所以我会有这样的情况,即对于给定的日记账分录,我会在那些不适用于该给定日记账分录的外键中有空值。
2) 我可以创建一个外键,我们称之为doc\u id,另一列doc\u type来指示文档的类型(发票、付款、账单、存款、,etc),并使doc\u id和document\u type\u id的组合引用其中一个扩展表上的主键(即doc\u id=1&doc\u type=invoice,该组合将引用invoice表上的主键)。
哪种方法更好?还是我想的都错了?

ltskdhd1

ltskdhd11#

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

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是主键,因此是唯一的。因此,从域定义的Angular 来看,使id和etype的组合唯一的约束是多余的。它所做的只是为外键定义一个锚点。
这些FK将在子实体表中--每个子实体一个表:发票、付款、账单和存款。请注意,如果在journalentries表中将条目定义为存款(etype='d'),则只能在deposits表中创建相应的条目。例如,您不能在payments表中错误地使用该id。
让我们定义一个子实体表:

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条目匹配。

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

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

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

请注意,“日记条目”是与其中一个子实体表中的关联数据相关联的日记数据。因此,对任何日记条目的fk引用都应该引用journalentries表,而不是任何子实体表,即使您知道它是什么类型的条目。所以activities行引用journalentries表,使用etype字段作为额外的数据完整性工作,因为它必须是发票。交集表包含任何类型的条目,因此它的fk目标就是pk。
注意:为了便于说明,journalentries表中的类型指示符被check语句约束。在实际的数据库中,更好的设计是条目类型查找表。这样可以保持数据的完整性,但设计要灵活得多(加上mysql仍然(!)不实现检查约束。)

相关问题