私が仕様を正しく理解している場合、シナリオには(他の重要な側面の中で)スーパータイプとサブタイプの構造が含まれます。
以下に、(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の図は、前述のすべてのビジネスルールと、関連する他のビジネスルールを統合したものです。
示されているように、グループとユーザーは、それぞれの線によって接続されたサブタイプと、スーパータイプである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
です。たとえばgroup
、title
「テストグループ」を含む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が作成した論理データベース設計手法についても説明します。