aar*_*nes 8 postgresql database-design subtypes
在相关的业务背景,既成员和组织需要有一个帐户的资金。资金可以转移
注意事项
为了为这种场景构建数据库,我创建了以下三个表:
CREATE TABLE Members (
memberid serial primary key,
name varchar(50) unique,
passwd varchar(32),
account integer
);
CREATE TABLE Organizations (
organizationid serial primary key,
name varchar(150) unique,
administrator integer references Members(memberid),
account integer
);
CREATE TABLE TransferHistory
"from" integer, -- foreign key?
"to" integer, -- foreign key?
quantity integer
);
Run Code Online (Sandbox Code Playgroud)
我认为TransferHistory
表是必要的,以显示谁/什么发基金向谁/什么。
问题是,由于Members
和Organizations
是不同的表,我如何从TransferHistory
表中引用它们?
例如,所涉及的数据可以显示如下:
Account Account Quantity
----------- ----------- --------
1072561733 38574637847 500
38574637847 1072561733 281
Run Code Online (Sandbox Code Playgroud)
这表明帐户需要记录在同一个表中,但帐户是针对两种不同类型的所有者(成员和组织),每种所有者都保留在各自的表中。
我可以创建一个名为 的表Accounts
,所以现在我将有四个表:
CREATE TABLE Members (
memberid serial primary key,
name varchar(50) unique,
passwd varchar(32),
accountid integer references Accounts(accountid)
);
CREATE TABLE Organizations (
organizationid serial primary key,
name varchar(150) unique,
administrator integer references Members(memberid),
accountid integer references Accounts(accountid)
);
CREATE TABLE Accounts (
accountid serial primary key,
state integer
);
CREATE TABLE TransferHistory (
"from" integer references Accounts(accountid),
"to" integer references Accounts(accountid),
quantity integer
);
Run Code Online (Sandbox Code Playgroud)
...但现在我必须确保来自Members
和Organizations
表的每个外键不指向表中的同一Account
行Accounts
......
...或者我可以有一个Accounts
表有两个外键,一个指向Members
另一个指向Organizations
(并且一个外键列必须始终包含一个 NULL 标记)。但是现在关于查询的事情变得有点混乱。一般而言,数据库设计如下:
CREATE TABLE Members (
memberid serial primary key,
name varchar(50) unique,
passwd varchar(32)
);
CREATE TABLE Organizations (
organizationid serial primary key,
name varchar(150) unique,
administrator integer references Members(memberid)
);
CREATE TABLE Accounts (
accountid serial primary key,
member integer references Members(memberid),
organization integer references Organizations(organizationid),
state integer
);
CREATE TABLE TransferHistory (
"from" integer references Accounts(accountid),
"to" integer references Accounts(accountid),
quantity integer
);
Run Code Online (Sandbox Code Playgroud)
那么,有人对如何解决这个问题有任何建议吗?
MDC*_*CCL 13
如果打算构建一个关系数据库,首先执行 (a) 对感兴趣的业务上下文的分析——为了描绘一个概念模式——在实体类型方面,在检查它们的属性和关联之前,真的很有帮助(b) 根据表、列和约束——对应于逻辑级别的方面——进行思考。按照这个操作过程,准确地捕获业务领域的含义,然后将其反映在实际的、约束良好的 SQL-DDL 设计中要简单得多。
关系范式提供的众多优势之一是它允许在其自然结构中管理数据;因此,在使用关系工具来管理它之前,必须“找到”这样的结构。所讨论的场景是否与个人项目相关并不重要(正如您通过评论指出的那样):您定义的越现实,您从其开发中学到的东西就越多(如果这就是这项工作的目的)。当然,一个现实的个人项目可能会演变成一个相对较小的改编的商业项目。
为了呈现您可能喜欢用作参考的第一个进展,我制定了一些最重要的概念级业务规则,并将它们列举如下:
由于协会-or relationships-(1)之间的人与账户之间;(2)组织和帐户非常相似,这一事实表明,人与账户是实体亚型的党(基本上,无论是个人或一群人) ,这反过来又是他们的实体超类型。这是一个经典的信息结构,在不同种类的多个概念模型中经常出现。通过这种方式,可以断言两个新规则:
之前的两个业务规则可以合并为一个:
也可以从账户的角度来表述实体类型:
因此,我创建了一个说明(简化)IDEF1X †图,它综合了上面制定的规则,如图 1所示:
党、人、组织:超类型-子类型结构
如图所示,Person
并且Organization
被描述为互斥的的亚型Party
。
所述Party
超型保持鉴别器(即,PartyTypeCode
)和所有所共有其亚型,这反过来,具有适用于它们中的每属性的属性(或属性)。
帐户
的Account
实体类型是直接与连接Party
,它提供之间(I)的随后的连接Account
和Person
之间,和(ⅱ)Account
和Organization
。
由于在现实世界中,(a) 银行Account
是不可转让的,即它Owner
不能更改,并且 (b)Account
不能在没有 的情况下开始当前或启用Owner
,因此该实体类型的 PRIMARY KEY可能包括属性PartyId
和 AccountNumber
,因此您应该更彻底地分析场景以高精度定义这一点。
转移
在另一方面,Transfer
实体类型提出了一个复合主键由三个属性,最多也就是TransferorAccountNumber
,TransfereeAccountNumber
(角色名称我分配了两个中的每一个区分Account
涉及每个属性Transfer
和实例)TransferDateTime
(它告诉exaxct即时当Transfer
发生率执行)。
关于帐号的因素
还要注意,在实际的银行系统中,AccountNumber
数据点的格式通常比“仅仅是”整数值更复杂。有不同的格式安排,例如,对应于ISO 13616定义的国际银行帐号 (IBAN) 的格式安排标准。这方面显然意味着 (1) 概念分析和后面的 (2) 逻辑定义需要更详尽的方法。
然后,作为之前分析的推导,我声明了一个逻辑设计,其中
我提供了注释作为注释,解释了我认为在上述结构方面特别重要的一些特征,如下所示:
-- You have to determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on the business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient physical implementation settings; e.g.,
-- a good INDEXing strategy based on query tendencies.
-- As one would expect, you are free to make use of
-- your preferred (or required) naming conventions.
CREATE TABLE PartyType (
PartyTypeCode CHAR(1) NOT NULL, -- This one is meant to contain the meaningful values 'P', for 'Person', and 'O' for 'Organization'.
Name CHAR(30) NOT NULL,
--
CONSTRAINT PartyType_PK PRIMARY KEY (PartyTypeCode)
);
CREATE TABLE Party ( -- Represents the supertype.
PartyId INT NOT NULL,
PartyTypeCode CHAR(1) NOT NULL, -- Denotes the subtype discriminator.
CreatedDateTime TIMESTAMP NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Party_PK PRIMARY KEY (PartyId),
CONSTRAINT PartyToPartyType_FK FOREIGN KEY (PartyTypeCode)
REFERENCES PartyType (PartyTypeCode)
);
CREATE TABLE Person ( -- Stands for a subtype.
PersonId INT NOT NULL, -- To be CONSTRAINed as PRIMARY KEY and FOREIGN KEY at the same time, enforcing an association cardinality of one-to-zero-or-one from Party to Person.
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
GenderCode CHAR(3) NOT NULL,
BirthDate DATE NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Person_PK PRIMARY KEY (PersonId),
CONSTRAINT Person_AK UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT PersonToParty_FK FOREIGN KEY (PersonId)
REFERENCES Party (PartyId)
);
CREATE TABLE Organization ( -- Represents the other subtype.
OrganizationId INT NOT NULL, -- To be CONSTRAINed as PRIMARY KEY and FOREIGN KEY simultaneously, enforcing a association cardinality of one-to-zero-or-one from Party to Organization.
Name CHAR(30) NOT NULL,
FoundingDate DATE NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Organization_PK PRIMARY KEY (OrganizationId),
CONSTRAINT Organization_AK UNIQUE (Name), -- ALTERNATE KEY.
CONSTRAINT OrganizationToParty_FK FOREIGN KEY (OrganizationId)
REFERENCES Party (PartyId)
);
CREATE TABLE UserProfile (
UserId INT NOT NULL, -- To be CONSTRAINed as PRIMARY KEY and FOREIGN KEY at the same time, enforcing an association cardinality of one-to-zero-or-one from Person to UserProfile.
UserName CHAR(30) NOT NULL,
CreatedDateTime TIMESTAMP NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK UNIQUE (Username),
CONSTRAINT UserProfileToPerson_FK FOREIGN KEY (UserId)
REFERENCES Person (PersonId)
);
CREATE TABLE Account (
AccountNumber INT NOT NULL,
OwnerPartyId INT NOT NULL, -- A role name assigned to PartyId in order to depict the meaning it carries in the context of an Account.
CreatedDateTime TIMESTAMP NOT NULL,
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Account_PK PRIMARY KEY (AccountNumber),
CONSTRAINT AccountToParty_FK FOREIGN KEY (OwnerPartyId)
REFERENCES Party (PartyId)
);
CREATE TABLE Transfer (
TransferorAccountNumber INT NOT NULL, -- Role name assigned to AccountNumber.
TransfereeAccountNumber INT NOT NULL, -- Role name assigned to AccountNumber
TransferDateTime TIMESTAMP NOT NULL,
Amount INT NOT NULL, -- Retains the Amount in Cents, but there are other possibilities.
Etcetera CHAR(30) NOT NULL,
--
CONSTRAINT Transfer_PK PRIMARY KEY (TransferorAccountNumber, TransfereeAccountNumber, TransferDateTime), -- Composite PRIMARY KEY.
CONSTRAINT TransferToTransferor_FK FOREIGN KEY (TransferorAccountNumber)
REFERENCES Account (AccountNumber),
CONSTRAINT TransferToTransferee_FK FOREIGN KEY (TransfereeAccountNumber)
REFERENCES Account (AccountNumber),
CONSTRAINT AccountsAreDistinct_CK CHECK (TransferorAccountNumber <> TransfereeAccountNumber),
CONSTRAINT AmountIsValid_CK CHECK (Amount > 0)
);
Run Code Online (Sandbox Code Playgroud)
如前所述,不需要在任何基表的列中保留不明确和有问题的 NULL 标记。
如果您想知道某个转账中涉及的Account是属于Organization还是Person,您可以在单个 SELECT 语句中通过例如 the 、 the和 theTransfer.TrasnferorAccountNumber
Account.PartyId
Party.PartyTypeCode
列。
为了确保一方最多可以拥有一个帐户(如评论中所述),那么您应该为该Account.PartyId
列修复一个 UNIQUE 约束。然而,在现实世界的场景中,例如在银行中,一个人可以拥有零个或多个Accounts,因此我认为一对零或一的关联并不现实。
如前所述,本答案中提出的方法应该用作参考,您可以自行扩展和调整。自然地,在概念层面进行的扩展和改编应该反映在逻辑模型中。
我在 (i)这个 db<>fiddle和 (ii)这个 SQL Fiddle 中测试了这个结构的声明,两者都在 PostgreSQL 9.6 上运行(你最初附加了这个数据库管理系统的标签)。
关于表 Party、Person 和 Organization 的完整性和一致性注意事项
对于上述布局,必须保证每个“超类型”行始终处于被其相应的“子类型”对应补充,进而,确保所述与包含在超类型值“子类型”的行是相容的“鉴别器“ 柱子。
以声明方式强制执行这种情况会非常方便和优雅,但不幸的是,没有一个主要的 SQL 平台提供适当的机制来这样做(据我所知)。因此,使用ACID TRANSACTIONS非常方便,以便在数据库中始终自信地满足这些条件。
如果您对出现超类型-子类型结构的其他业务领域感兴趣,您可能希望看到我对以下问题的回答
Transfer.Amount
。† 信息建模集成定义( IDEF1X ) 是一种非常值得推荐的数据建模技术,美国国家标准与技术研究院(NIST)于 1993 年 12 月将其确立为标准。它完全基于 (a) 关系模型的创始人,即EF Codd 博士撰写的一些早期理论著作;关于 (b)实体关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。
归档时间: |
|
查看次数: |
3045 次 |
最近记录: |