具有两种可能的所有者/父类型的实体的数据库架构?


8

我正在将PostgreSQL与Sequelize用作我的ORM。

我有一种类型User。第二种类型是Group,可以通过GroupMemberships表与任何数量的用户相关联。Users也可以拥有任意数量的Groups。

我的第三种类型Playlist可以属于UserOR或a group。为这种类型设计模式的最佳方法是什么,使其既可以具有所有者的一种,又可以具有所有者的任何一种?

我的第一遍创建了两个关联,但是一次只能填充一个。这可能可行,但似乎很麻烦,并且使查询变得困难。

附加信息

这是我对MDCCL通过评论发布的澄清请求的回复:

(1)如果一个播放列表是由给定资集团,可以说,这个播放列表是关系到一个一对多的用户,只要他们是会员这样的小组,对不对?

我相信这在技术上是正确的,但是这种一对多的关联并不明确存在。

(2)因此,一个特定的播放列表是否可以同时由一对多的拥有?

不,Playlist一对多不可能拥有一个Groups

(3)一个特定的播放列表是否可能由一对多拥有,同时又由不是该成员的一对多用户拥有?

没有,因为在(2)一到,许多从PlaylistGroup应该不存在。此外,如果a Playlist由a Group拥有,则不由a拥有User,反之亦然。一次仅一个所有者。

(4)用来唯一标识用户播放列表的属性是什么?

它们每个都有代理主键(id)和自然键(尽管不是主键)。这些是slug用于GroupPlaylistusername用于User

(5)能在特定播放列表遭受所有者的变化?

尽管我不打算将其作为一项功能(至少最初是这样),但我认为这可能会发生。

(6)Group.SlugPlaylist.Slug属性的含义是什么?它们的值是否稳定到足以定义为主键,或者它们经常更改?这两个属性的值以及User.Username必须唯一,对吗?

这些slug是各自实体的唯一,小写,带连字符的版本title。例如,group带有title“测试组”的a将具有slug“测试组”。重复项将附加增量整数。每当他们title改变时,这都会改变。我相信这意味着他们不会做出出色的主键吗?是的,slugs并且usernames在各自的表中是唯一的。

Answers:


9

如果我正确地理解了您的规范,那么您的方案将涉及(除其他重要方面之外)超类型-子类型结构。

我将在下面举例说明如何(1)在概念的抽象级别对其进行建模,然后(2)在逻辑级别的 DDL设计中表示它。

商业规则

以下概念表述是您的业务环境中最重要的规则:

  • 播放列表由或恰好一个拥有或恰好一个用户在特定时间点
  • 一个播放列表可以由一个一对多所拥有的用户在不同的时间点
  • 一个用户拥有零一或一对多的播放列表
  • 拥有零个或多个播放列表
  • 由一到多的达议员(谁必须为用户
  • 一个用户可能是一个会员的零一或一对多的群组
  • 由一到多的达议员(谁必须为用户

由于之间的关联或关系(一)用户播放列表和(b)之间的集团播放列表都不太一样,这个事实表明,用户相互排斥的实体亚型1,这是在把自己的实体超-supertype-子类型集群是经典的数据结构,它们以非常多种形式的概念模式出现。通过这种方式,可以断言两个新规则:

  • 一个正好由一个分类PartyType
  • 一个可以是一个用户

前面的四个规则必须重新定义为三个:

  • 一个播放列表正好由一个拥有在特定时间点
  • 一个播放列表可以由一个一对多所拥有缔约方在不同的时间点
  • 拥有零一或一对多的播放列表

信息库IDEF1X图表

图1中显示的IDEF1X 2图将所有上述业务规则与其他相关的业务规则合并在一起:

图1-播放列表所有者IDEF1X示意图

如图所示,GroupUser被描绘为子类型,这些子类型由相应的行和排他性符号与Party(超类型)连接。

Party.PartyTypeCode物业代表亚型鉴别,即,它表示一种亚型实例必须补充一个给定的超发生。

而且,Party通过OwnerId属性与播放列表连接,该属性被描述为指向Party.PartyId的FOREIGN KEY 。这样,Party将(a)播放列表与(b)Group和(c)User相关联

因此,由于特定的Party实例是GroupUser,所以特定的播放列表最多可以与一个子类型出现链接。

说明性逻辑层布局

之前阐述的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)  
);

当然,您可以进行一项或多项调整,以便在实际数据库中以所需的精度表示业务上下文的所有特征。

注意:我已经在db <> fiddle此SQL Fiddle上测试了上述逻辑布局,它们都在PostgreSQL 9.6上“运行”,以便您可以看到它们“正在运行”。

Sl

如您所见,我没有在DDL声明中包括Group.Slug也不Playlist.Slug作为列。这是因为,按照您的以下解释

这些slug是各自实体的唯一,小写,带连字符的版本title。例如,group带有title“测试组”的a将具有slug“测试组”。重复项将附加增量整数。每当他们title改变时,这都会改变。我相信这意味着他们不会做出出色的主键吗?是的,slugs并且usernames在各自的表中是唯一的。

可以得出结论,它们的值是可导的(即,它们必须根据对应的Group.TitlePlaylist.Title值进行计算或计算,有时与(我认为是某种系统生成的INTEGER)结合使用),因此我不会声明所述列在任何基本表中,因为它们会引入更新不规则性。

相反,我会产生 Slugs

  • 在一个视图中,也许(a)包括在虚拟列中的此类值的推导,并且(b)可以直接在进一步的SELECT操作中使用-例如,可以通过组合(1)的值来获得INTEGER部分在Playlist.OwnerId与(2)的中间连字符和(3)的值Playlist.Title;

  • 或者,一旦检索到相关数据集并对其进行了格式设置以供最终用户解释,则可以通过应用程序代码模仿之前描述的方法(可能是过程上的方法)。

以这种方式,任何这两种方法将避免了“更新同步”机制,应该到位当且仅当所述Slugs被保持在基表的列。

完整性和一致性考虑

关键是要提及的是(ⅰ) Party行必须补充在任何时候都通过(ii)所述相应的对应物在恰好一个站立的亚型,其(III)必须“符合”的表中所包含的值Party.PartyTypeCode的列-表示区分符-。

声明的方式实施这种情况将是非常有利的,但是没有一个主要的SQL数据库管理系统(包括Postgres)提供了必要的工具来像这样进行。因此,到目前为止,在ACID TRANSACTIONS内编写过程代码是确保数据库中始终满足上述情况的最佳选择。另一种可能性是求助于TRIGGERS,但是可以这么说,他们倾向于使事情变得不整洁。

可比案例

如果您想建立一些类比,则可能有兴趣查看我对题为(较新)问题的回答

因为讨论了可比较的方案。


尾注

1 当事人是指在法律上下文中指组成单个实体个人或一组个人时使用的术语,因此该名称适用于代表有关业务环境的用户的概念。

2 信息建模集成定义 IDEF1X)是一种高度推荐的数据建模技术,该技术已于1993年12月由美国国家标准技术研究院(NIST)建立为标准。它扎实地基于(a)由关系模型的唯一发起人,即 EF Codd博士撰写的一些早期理论著作;(b)由陈PP博士提出的关于实体关系的观点;以及(c)Robert G. Brown创建的逻辑数据库设计技术。


我没有看到派对的任何约束完全是用户或组之一
Jasen

您的评论似乎与以下问题摘录有关(均包含在“完整性和一致性注意事项”部分中):
MDCCL

关键是要提及的是(ⅰ)每Party行必须补充在任何时候都通过(ii)所述相应的对应物在恰好放置的亚型,其(III)必须的‘符合’的表中的一个中包含的值Party.PartyTypeCode列-表示鉴别符- ”。
MDCCL '19

声明的方式实施这种情况将是非常有利的,但是没有一个主要的SQL数据库管理系统(包括Postgres)提供了必要的工具来像这样进行;因此,到目前为止,在ACID TRANSACTIONS内编写过程代码是确保您的数据库中始终满足上述情况的最佳选择。另一种可能性是求助于TRIGGERS,但是可以说,它们容易使事情变得不整洁
MDCCL

因此,一旦(希望很快)提供Postgres,例如ASSERTIONS,我将很高兴加入适用的(声明性)约束。目前,上面建议了两种可用的DBMS提供的方法。
MDCCL
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.