2つの可能な所有者/親タイプを持つエンティティのデータベーススキーマ?


8

私はSequelizeをORMとしてPostgreSQLを使用しています。

1つのタイプがありUserます。2番目のタイプはGroupで、GroupMembershipsテーブルを介して任意の数のユーザーを関連付けることができます。Userは、任意の数のを所有することもできGroupます。

3番目のタイプはPlaylistUserORまたはaのいずれかに属することができgroupます。このタイプのスキーマを設計して、1つのタイプの所有者またはいずれかのタイプの所有者を持つことができる最善の方法は何ですか?

最初のパスでは両方の関連付けを作成しましたが、一度に1つだけ入力しました。これは機能する可能性がありますが、ハックに見え、クエリを困難にします。

追加情報

コメントを介してMDCCLによって投稿された説明要求に対する私の応答は次のとおりです。

(1)プレイリストが特定のグループによって所有されている場合、このプレイリストは、そのグループのメンバーである限り、1 対多のユーザーに関連していると言えますか?

これは技術的には正しいと思いますが、この1対多の関連付けは明示的には存在しません。

(2)では、特定のプレイリストを1 対多のグループが同時に所有することは可能ですか?

いいえ、をPlaylist1対多で所有することはできませんGroups

(3)特定のプレイリストを1 対多のグループ、およびそのようなグループのメンバーではない1 対多のユーザーが所有することは可能ですか?

いいえ。(2)のように、1対多のto が存在しPlaylistGroupはならないためです。さらに、Playlistがによって所有されてGroupいる場合、は所有していませんUser。逆も同様です。一度に1人の所有者のみ。

(4)グループユーザープレイリストを一意に識別するために使用されるプロパティは何ですか?

それぞれに代理主キー(id)と自然キー(主ではない)があります。これらはslugGroupおよびPlaylist、およびにusername対応していUserます。

(5)特定のプレイリスト所有者が変更される可能性はありますか?

私はこれが機能であることを計画していませんが(少なくとも最初は)、これは仮説的に発生する可能性があります。

(6)Group.SlugおよびPlaylist.Slug属性の意味は何ですか?それらの値は、主キーとして定義されるのに十分安定していますか、それとも頻繁に変更されますか?これら2つのプロパティの値は、User.Usernameとともに一意である必要がありますか?

これらslugのは、固有の小文字のハイフン付きのバージョンであり、それぞれのエンティティのtitleです。たとえばgrouptitle「テストグループ」を含むa は「テストグループ」を持ちslugます。重複には増分整数が追加されます。これは彼らのtitle変化がいつでも変わるでしょう。私はそれが彼らが素晴らしい主キーを作成しないことを意味すると思いますか?はい、slugsそしてusernames、それぞれのテーブルにユニークです。

回答:


9

私が仕様を正しく理解している場合、シナリオには(他の重要な側面の中で)スーパータイプとサブタイプの構造が含まれます。

以下に、(1)抽象化の概念レベルでモデル化し、(2)論理レベルの DDL設計でそれを表現する方法を例示します。

ビジネスルール

次の概念的な定式化は、ビジネスコンテキストで最も重要なルールの1つです。

  • A プレイリストは正確に一つのいずれかによって所有され、グループまたは正確に一つのユーザーの時間に特定のポイントで
  • A プレイリストは、一対多によって所有されてもよいグループまたはユーザーの時間に異なる点で
  • ユーザーは、ゼロ・ワン・オア・多く所有しているプレイリストを
  • A グループは、ゼロ・ワン・オア・多く所有しているプレイリストを
  • A グループは、一対多で構成されているメンバー(でなければならないユーザー
  • ユーザーはかもしれ加盟ゼロ-1-または-多くのグループ
  • A グループは、一対多で構成されているメンバー(でなければならないユーザー

間の関連または関係()としてユーザープレイリストの間および(b)のグループプレイリストが非常に似ている、この事実は、ことが明らかになったユーザーグループがあり、相互に排他的なの実体サブタイプパーティー1彼らの実体を回すスーパータイプ-supertype-であり、サブタイプクラスターは、非常に多様な種類の概念スキーマで生じる古典的なデータ構造です。この方法で、2つの新しいルールをアサートできます。

  • A 党は正確に一つによって分類されPartyType
  • A 党はどちらかであるグループまたはユーザー

また、前述のルールのうち4つを3つだけに再構成する必要があります。

  • A プレイリストは正確に一つが所有している特定の時点で
  • A プレイリストは、一対多によって所有されてもよい当事者時間に異なる点で
  • A 党はゼロ-1-または-多く所有しているプレイリストを

解説IDEF1X図

図1に示すIDEF1X 2の図は、前述のすべてのビジネスルールと、関連する他のビジネスルールを統合したものです。

図1-プレイリスト所有者のIDEF1Xダイアグラム

示されているように、グループユーザーは、それぞれの線によって接続されたサブタイプと、スーパータイプであるPartyとの排他的なシンボルとして描かれています。

Party.PartyTypeCodeのプロパティは、サブタイプを意味弁別すなわち、それは与えられたスーパータイプの発生を補完しなければならないサブタイプのインスタンスの種類を示し、。

また、Partyは、Party.PartyIdを指すFOREIGN KEYとして示されるOwnerIdプロパティを介してプレイリストに接続されます。このようにして、パーティーは(a)プレイリストと(b)グループおよび(c)ユーザーを相互に関連付けます

したがって、特定のパーティーインスタンスはグループまたはユーザーのいずれかであるため、特定のプレイリストは最大で1つのサブタイプオカレンスにリンクできます。

例示的な論理レベルのレイアウト

以前に説明した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)  
);

もちろん、ビジネスコンテキストのすべての特性が実際のデータベースで必要な精度で表されるように、1つ以上の調整を行うことができます。

:私は上の上記論理的なレイアウトをテストしているDB <>フィドルこのともに、このSQLフィドル、両方のあなたは「アクションに」それらを見ることができるように、PostgreSQLの9.6上で「実行中」。

スラグ

ご覧のとおり、DDL宣言には列Group.Slugも含めませんでしたPlaylist.Slug。これは、以下の説明に同意して、

これらslugのは、固有の小文字のハイフン付きのバージョンであり、それぞれのエンティティのtitleです。たとえばgrouptitle「テストグループ」を含むa は「テストグループ」を持ちslugます。重複には増分整数が追加されます。これは彼らのtitle変化がいつでも変わるでしょう。私はそれが彼らが素晴らしい主キーを作成しないことを意味すると思いますか?はい、slugsそしてusernames、それぞれのテーブルにユニークです。

それらの値は導出可能であると結論付けることができます(つまり、それらは対応するGroup.TitleおよびPlaylist.Title値に関して計算または計算する必要があり、場合によっては、システムで生成されたある種のINTEGERと組み合わせて)、そのため、上記の列は宣言しません更新の不規則性を導入するため、ベーステーブルのいずれか。

対照的に、私は Slugs

  • 多分、ビューでは、(a)仮想列にそのような値の導出が含まれ、(b)後続のSELECT操作で直接使用できます。たとえば、(1)の値を組み合わせることにより、INTEGER部分を追加できます。Playlist.OwnerId(2)中間体ハイフンおよび(3)の値を持ちますPlaylist.Title

  • または、アプリケーションプログラムコードのおかげで、適切なデータセットがエンドユーザーの解釈のために取得およびフォーマットされると、前述のアプローチを模倣します(おそらく手続き的に)。

この方法では、これらの2つの方法のいずれでも、ベーステーブルの列に保持されている場合に配置されるべき「更新同期」メカニズムが回避Slugsされます。

整合性と一貫性に関する考慮事項

(i)がいることを言及することが重要であり、 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が作成した論理データベース設計手法についても説明します。


パーティーの制約がユーザーまたはグループの1つに
すぎ

あなたのコメントは、次の質問の抜粋(どちらも「整合性と整合性に関する考慮事項」というタイトルのセクションに含まれています)と関係があるようです:
MDCCL

(i)Party各行は常に(ii)サブタイプを表すテーブルの1つParty.PartyTypeCodeだけにある対応するものによって補完れる必要があることに言及することが重要です。(iii)これは、(iii)列-弁別子の表記-」。
MDCCL

このような状況を宣言的な方法で強制することは非常に有利ですが、主要なSQLデータベース管理システム(Postgresを含む)はどれも、そのように進めるために必要な手段を提供していません。したがって、これまでのところ、ACID TRANSACTIONS内で手続き型コードを記述することは、前述の状況がデータベースで常に満たされることを保証する最良のオプションです。他の可能性はトリガーに頼ることですが、いわば、物事を整然とする傾向があります。
MDCCL

したがって、(できればすぐに)PostgresがASSERTIONSを提供するとしたら、適用可能な(宣言的な)制約を含めて喜んでいます。当面は、DBMSが提供する2つの利用可能なアプローチを上記に示します。
MDCCL
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.