我有一个带有内置完整会计系统的物业管理应用程序。我有一个日记帐分录表,该表控制各种会计活动的所有过帐,例如:
发票
付款方式
账单
存款
在某些情况下,有必要将这些实体连接到日记帐分录表,以按不同的属性和单位汇总会计分录。
我正在寻找做到这一点的最佳方法。我有几种选择:
1)在日记帐分录表上添加一个外键以链接到invoice_id,payment_id,bill_id,deposit_id,但是这些的大多数组合都是互斥的(即,押金不会付款),因此在某些情况下给定日记条目,我将在那些不适用于给定日记条目的外键中使用null。
2)我可以创建一个外键,将其称为doc_id和另一列doc_type来指示单据类型(发票,付款,账单,存款等),并结合doc_id和document_type_id来引用主键扩展表之一(即doc_id = 1和doc_type = Invoice,该组合将引用Invoice表上的主键)。
哪个是解决此问题的更好方法,还是我在考虑所有这些错误?
最佳答案
这听起来像是标准的基础实体/子实体模式。这里有一个表,我们称它为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是主键,因此是唯一的。因此,从域定义的角度来看,使ID和EType的组合唯一的约束是多余的。它所做的只是为外键定义锚。
这些FK将在子实体表中-每个子实体一个表:发票,付款,账单和存款。请注意,如果在JournalEntries表中将条目定义为“存款”(EType ='D'),则只能在“存款”表中进行相应的条目。例如,您不能在“付款”表中错误地使用该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 )
);
请注意,“ Journal Entry”是JournalEntries数据与来自子实体表之一的关联数据结合在一起的数据。因此,FK对任何日记帐分录的引用都应引用JournalEntries表,而不是任何子实体表,即使您知道它是哪种分录。因此,“活动”行使用EType字段引用JournalEntries表,这是附加的数据完整性工作,因为它必须是发票。相交表包含任何类型的条目,因此其FK目标只是PK。
注意:出于说明目的,JournalEntries表中的类型指示符受一个检查语句约束。在实际的数据库中,一个更好的设计是条目类型查找表。这样可以保持数据完整性,但设计灵活得多。 (加上MySQL still(!)没有实现检查约束的事实。)