Jak*_*rsh 8 schema postgresql database-design subtypes
我使用带有Sequelize 的PostgreSQL作为我的 ORM。
我有一种类型,User。第二种类型是Group,它可以通过一个GroupMemberships表将任意数量的用户与其关联。Users 也可以拥有任意数量的Groups。
我的第三种类型 ,Playlist可以属于 aUser或 a group。为这种类型设计模式以便它可以拥有一种类型的所有者或两者之一的最佳方法是什么?
我的第一遍创建了两个关联,但一次只填充一个。这可能有效,但看起来很笨拙并且使查询变得困难。
附加信息
以下是我对 MDCCL 通过评论发布的澄清请求的回应:
(1)如果一个播放列表是由给定资集团,可以说,这个播放列表是关系到一个一对多的用户,只要他们是会员这样的小组,对不对?
我相信这在技术上是正确的,但这种一对多关联并不明确存在。
(2) 那么,一个特定的播放列表是否有可能同时被一对多的群组拥有?
不, a 应该不可能被Playlist一对多拥有Groups。
(3) 特定播放列表是否可能由一对多组拥有,同时由不是该组成员的一对多用户拥有?
不,因为如 (2) 中的一对多 from PlaylisttoGroup不应该存在。此外,如果 aPlaylist归a所有,Group则它不归 a 拥有User,反之亦然。一次只有一个所有者。
(4) 用于唯一标识Group、User和Playlist的属性是什么?
它们每个都有一个代理主键 ( id),以及一个自然键(虽然不是主键)。这些是slugforGroup和Playlist,和usernamefor User。
(5) 特定的播放列表是否会遭受所有者更改?
虽然我不打算将此作为一个功能(至少在最初),但我认为这可能会发生。
(6) Group.Slug和Playlist.Slug属性是什么意思?它们的值是否足够稳定以定义为主键还是经常更改?这两个属性的值以及User.Username必须是唯一的,对吗?
这些slugs 是它们各自实体的title. 例如,group带有title“测试组”的将具有slug“测试组”。重复项附加有增量整数。这会随时改变他们的title变化。我相信这意味着他们不会制作很好的主键?是的,slugs并且usernames在各自的表中是唯一的。
MDC*_*CCL 10
如果我正确理解您的规范,那么您的场景包括——在其他重要方面——超类型-子类型结构。
我将在下面举例说明如何 (1) 在抽象的概念级别对其建模,然后 (2) 在逻辑级别的DDL 设计中表示它。
以下概念公式是您业务环境中最重要的规则之一:
由于 (a) User和Playlist之间和 (b) Group和Playlist之间的关联或关系非常相似,这一事实表明User和Group是Party 1 的互斥实体子类型,而Party 1又是它们的实体超类型 — supertype-子类型集群是经典的数据结构,出现在非常多样化的概念模式中。通过这种方式,可以断言两个新规则:
之前的四项规则必须重新表述为只有三项:
说明性IDEF1X图
图 1 中显示的 IDEF1X 2图表整合了所有上述业务规则以及其他相关的业务规则:

正如所演示的,Group和User被描绘为子类型,它们通过各自的行和与Party的专有符号连接,超类型。
该Party.PartyTypeCode物业代表亚型鉴别,即,它表示一种亚型实例必须补充一个给定的超发生。
此外,Party通过OwnerId属性与播放列表连接,该属性被描述为指向Party.PartyId的 FOREIGN KEY 。这样,Party 将(a) Playlist与 (b) Group和 (c) User相互关联。
因此,由于特定的Party实例是Group或User,因此特定的播放列表最多可以与一个子类型出现链接。
之前阐述的 IDEF1X 图表为我提供了一个平台来创建以下逻辑 SQL-DDL 安排(并且我提供了注释作为注释突出了几个特别相关的点——例如,约束声明——):
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient INDEX strategies based on the exact
-- data manipulation tendencies of your business domain.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE PartyType ( -- Represents an independent entity type.
PartyTypeCode CHAR(1) NOT NULL,
Name CHAR(30) NOT NULL,
--
CONSTRAINT PartyType_PK PRIMARY KEY (PartyTypeCode),
CONSTRAINT PartyType_AK UNIQUE (Name)
);
CREATE TABLE Party ( -- Stands for the supertype.
PartyId INT NOT NULL,
PartyTypeCode CHAR(1) NOT NULL, -- Symbolizes the discriminator.
CreatedDateTime TIMESTAMP NOT NULL,
--
CONSTRAINT Party_PK PRIMARY KEY (PartyId),
CONSTRAINT PartyToPartyType_FK FOREIGN KEY (PartyTypeCode)
REFERENCES PartyType (PartyTypeCode)
);
CREATE TABLE UserProfile ( -- Denotes one of the subtypes.
UserId INT NOT NULL, -- To be constrained as both (a) the PRIMARY KEY and (b) a FOREIGN KEY.
UserName CHAR(30) NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
GenderCode CHAR(3) NOT NULL,
BirthDate DATE NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK1 UNIQUE ( -- Multi-column ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT UserProfile_AK2 UNIQUE (UserName), -- Single-column ALTERNATE KEY.
CONSTRAINT UserProfileToParty_FK FOREIGN KEY (UserId)
REFERENCES Party (PartyId)
);
CREATE TABLE MyGroup ( -- Represents the other subtype.
GroupId INT NOT NULL, -- To be constrained as both (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Title CHAR(30) NOT NULL,
--
CONSTRAINT Group_PK PRIMARY KEY (GroupId),
CONSTRAINT Group_AK UNIQUE (Title), -- ALTERNATE KEY.
CONSTRAINT GroupToParty_FK FOREIGN KEY (GroupId)
REFERENCES Party (PartyId)
);
CREATE TABLE Playlist ( -- Stands for an independent entity type.
PlaylistId INT NOT NULL,
OwnerId INT NOT NULL,
Title CHAR(30) NOT NULL,
CreatedDateTime TIMESTAMP NOT NULL,
--
CONSTRAINT Playlist_PK PRIMARY KEY (PlaylistId),
CONSTRAINT Playlist_AK UNIQUE (Title), -- ALTERNATE KEY.
CONSTRAINT PartyToParty_FK FOREIGN KEY (OwnerId) -- Establishes the relationship with (a) the supertype and (b) through the subtype with (c) the subtypes.
REFERENCES Party (PartyId)
);
CREATE TABLE GroupMember ( -- Denotes an associative entity type.
MemberId INT NOT NULL,
GroupId INT NOT NULL,
IsOwner BOOLEAN NOT NULL,
JoinedDateTime TIMESTAMP NOT NULL,
--
CONSTRAINT GroupMember_PK PRIMARY KEY (MemberId, GroupId), -- Composite PRIMARY KEY.
CONSTRAINT GroupMemberToUserProfile_FK FOREIGN KEY (MemberId)
REFERENCES UserProfile (UserId),
CONSTRAINT GroupMemberToMyGroup_FK FOREIGN KEY (GroupId)
REFERENCES MyGroup (GroupId)
);
Run Code Online (Sandbox Code Playgroud)
当然,您可以进行一项或多项调整,以便在实际数据库中以所需的精度表示业务环境的所有特征。
注意:我已经在这个 db<>fiddle和这个 SQL Fiddle上测试了上面的逻辑布局,两者都在 PostgreSQL 9.6 上“运行”,所以你可以看到它们“运行”。
鼻涕虫
如您所见,我没有在 DDL 声明中包含Group.Slug或Playlist.Slug作为列。这是因为,与您的以下解释一致
这些
slugs 是它们各自实体的title. 例如,group带有title“测试组”的将具有slug“测试组”。重复项附加有增量整数。这会随时改变他们的title变化。我相信这意味着他们不会制作很好的主键?是的,slugs并且usernames在各自的表中是唯一的。
人们可以得出结论,它们的值是可推导的(即,它们必须根据相应的Group.Title和Playlist.Title值进行计算或计算,有时结合——我假设,某种系统生成的——整数),所以我不会声明所述列在任何的基表,因为他们将推出更新的违规行为。
相反,我会产生 Slugs
也许,在一个视图中,其中 (a) 包括这些值在虚拟列中的推导,并且 (b) 可以直接用于进一步的 SELECT 操作——附加 INTEGER 部分可以获得,例如,通过组合 (1) 的值在Playlist.OwnerId与(2)的中间连字符和(3)的值Playlist.Title;
或者,凭借应用程序代码,模仿之前描述的方法(可能是程序上的),一旦相关数据集被检索并格式化以供最终用户解释。
以这种方式,任何这两种方法将避免了“更新同步”机制,应该到位当且仅当所述Slugs被保持在基表的列。
完整性和一致性注意事项
关键是要提及的是(ⅰ)每 Party行必须补充在任何时候都通过(ii)所述相应的对应物在恰好一个站立的亚型,其(III)必须“符合”的表中所包含的值Party.PartyTypeCode的列——表示鉴别器——。
以声明方式强制执行这种情况将是非常有利的,但是没有一个主要的 SQL 数据库管理系统(包括 Postgres)提供必要的工具来进行这样的操作;因此,迄今为止,在ACID TRANSACTIONS 中编写过程代码是保证数据库中始终满足前面描述的情况的最佳选择。其他可能性是诉诸触发器,但可以这么说,它们容易使事情变得不整洁。
如果你想建立一些类比,你可能有兴趣看看我对题为的(较新的)问题的回答
因为讨论了类似的场景。
1 方是在法律上下文中使用的一个术语,用于指代组成单个实体的个人或个人团体,因此该名称适合代表用户和团体关于相关商业环境的概念。
2 对于信息建模集成定义( IDEF1X)是被确立为一个非常可取的数据建模技术标准是由美国在1993年12月美国国家标准与技术研究院(NIST)。它完全基于 (a)关系模型的唯一创始人,即EF Codd 博士撰写的一些早期理论著作;关于 (b)实体关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。
| 归档时间: |
|
| 查看次数: |
1750 次 |
| 最近记录: |