Si je comprends bien vos spécifications, votre scénario implique - parmi d'autres aspects importants - une structure supertype-sous-type .
Je vais illustrer ci-dessous comment (1) le modéliser au niveau conceptuel de l'abstraction et (2) le représenter par la suite dans une conception DDL au niveau logique .
Règles métier
Les formulations conceptuelles suivantes sont parmi les règles les plus importantes dans votre contexte commercial:
- Une liste de lecture appartient à exactement un groupe ou exactement un utilisateur à un moment donné
- Une liste de lecture peut appartenir à un ou plusieurs groupes ou utilisateurs à des moments différents
- Un utilisateur possède zéro ou une ou plusieurs listes de lecture
- Un groupe possède zéro ou une ou plusieurs listes de lecture
- Un groupe est composé d'un à plusieurs membres (qui doivent être des utilisateurs )
- Un utilisateur peut être membre de groupes zéro-un ou plusieurs .
- Un groupe est composé d'un à plusieurs membres (qui doivent être des utilisateurs )
Étant donné que les associations ou relations (a) entre l' utilisateur et la liste de lecture et (b) entre le groupe et la liste de lecture sont assez similaires, ce fait révèle que l' utilisateur et le groupe sont des sous-types d'entités mutuellement exclusifs de la Partie 1 , qui est à son tour leur supertype d'entité —supertype- les clusters de sous-types sont des structures de données classiques qui apparaissent dans des schémas conceptuels de types très divers. De cette manière, deux nouvelles règles peuvent être affirmées:
- Une Party est catégorisée par exactement un PartyType
- Une partie est un groupe ou un utilisateur
Et quatre des règles précédentes doivent être reformulées en seulement trois:
- Une liste de lecture appartient à une seule partie à un moment précis
- Une liste de lecture peut appartenir à une ou plusieurs parties à des moments différents
- Une partie possède zéro ou une ou plusieurs listes de lecture
Diagramme expositoire IDEF1X
Le diagramme IDEF1X 2 illustré à la figure 1 consolide toutes les règles métier susmentionnées ainsi que d'autres qui semblent pertinentes:
Comme démontré, le groupe et l' utilisateur sont représentés comme des sous-types qui sont connectés par les lignes respectives et le symbole exclusif avec Party , le supertype.
La propriété Party.PartyTypeCode représente le discriminateur de sous-type , c'est-à-dire qu'elle indique quel type d'instance de sous-type doit compléter une occurrence de supertype donnée.
De plus, Party est connecté à Playlist via la propriété OwnerId qui est représentée comme une CLÉ ÉTRANGÈRE qui pointe vers Party.PartyId . De cette manière, le Parti met en corrélation (a) la Playlist avec (b) le Groupe et (c) l' Utilisateur .
En conséquence, étant donné qu'une instance de partie particulière est soit un groupe soit un utilisateur , une liste de lecture spécifique peut être liée à au plus une occurrence de sous-type.
Présentation illustrative au niveau logique
Le diagramme IDEF1X exposé précédemment m'a servi de plate-forme pour créer l'arrangement SQL-DDL logique suivant (et j'ai fourni des notes sous forme de commentaires mettant en évidence plusieurs points particulièrement pertinents - par exemple, les déclarations de contraintes -):
-- 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)
);
Bien sûr, vous pouvez effectuer un ou plusieurs ajustements afin que toutes les caractéristiques de votre contexte commercial soient représentées avec la précision requise dans la base de données réelle.
Remarque : J'ai testé la disposition logique ci-dessus sur ce violon db <> et également sur ce SQL Fiddle , tous deux «en cours d'exécution» sur PostgreSQL 9.6, afin que vous puissiez les voir «en action».
Les limaces
Comme vous pouvez le voir, je n'ai pas inclus Group.Slug
ni Playlist.Slug
colonnes dans les déclarations DDL. Il en est ainsi parce que, en accord avec votre explication suivante
Ces slug
s sont des versions uniques, en minuscules, avec trait d'union de leurs entités respectives title
. Par exemple, un group
avec le title
'Test Group' aurait le slug
'test-group'. Les doublons sont ajoutés avec des entiers incrémentiels. Cela changerait à tout moment leurs title
changements. Je crois que cela signifie qu'ils ne feraient pas de bonnes clés primaires? Oui, slugs
et usernames
sont uniques dans leurs tableaux respectifs.
on peut conclure que leurs valeurs sont dérivables (c'est-à-dire qu'elles doivent être calculées ou calculées en fonction des valeurs correspondantes Group.Title
et Playlist.Title
, parfois en conjonction avec — je suppose, une sorte de système généré — INTEGER), donc je ne déclarerais pas lesdites colonnes dans n'importe laquelle des tables de base car elles introduiraient des irrégularités de mise à jour.
En revanche, je produirais le Slugs
peut-être, dans une vue , qui (a) inclut la dérivation de ces valeurs dans des colonnes virtuelles et (b) peut être utilisée directement dans d'autres opérations SELECT - l'ajout de la partie INTEGER pourrait être obtenu, par exemple, en combinant la valeur de (1) le Playlist.OwnerId
avec (2) les tirets intermédiaires et (3) la valeur du Playlist.Title
;
ou, en vertu du code du programme d'application, imitant l'approche décrite précédemment (peut-être de manière procédurale), une fois que les ensembles de données pertinents sont récupérés et formatés pour une interprétation par l'utilisateur final.
De cette manière, l' une de ces deux méthodes éviterait le mécanisme « de synchronisation de mise à jour » qui devrait être mis en place ssi le Slugs
sont conservés dans les colonnes de tables de base.
Considérations d'intégrité et de cohérence
Il est essentiel de mentionner que (i) chaque Party
ligne doit être complétée à tout moment par (ii) la contrepartie respective dans exactement l'un des tableaux représentant les sous-types, qui (iii) doit «respecter» la valeur contenue dans la Party.PartyTypeCode
colonne —Indiquant le discriminateur—.
Il serait tout à fait avantageux d'appliquer ce type de situation de manière déclarative , mais aucun des principaux systèmes de gestion de base de données SQL (y compris Postgres) n'a fourni les instruments nécessaires pour procéder de la sorte; par conséquent, l'écriture de code procédural dans ACID TRANSACTIONS est jusqu'à présent la meilleure option pour garantir que les circonstances décrites précédemment sont toujours respectées dans votre base de données. Une autre possibilité serait de recourir à des déclencheurs, mais ils sont enclins à rendre les choses désordonnées, pour ainsi dire.
Cas comparables
Si vous souhaitez établir des analogies, vous pourriez être intéressé à jeter un œil à mes réponses aux (nouvelles) questions intitulées
puisque des scénarios comparables sont discutés.
Notes de fin
1 Partie est un terme utilisé dans des contextes juridiques pour désigner un individu ou un groupe d'individus qui composent une seule entité , de sorte que cette dénomination convient pour représenter les concepts d' utilisateur et de groupe par rapport à l'environnement commercial en question.
2 La définition d'intégration pour la modélisation de l'information ( IDEF1X ) est une technique de modélisation de données hautement recommandable qui a été établie comme norme en décembre 1993 par le National Institute of Standards and Technology (NIST)des États-Unis. Il est solidement basé sur (a) certains des premiers travaux théoriques rédigés par le seul auteur du modèle relationnel , à savoir le Dr EF Codd ; sur (b) la vue entité-relation , développée par le Dr PP Chen ; et également sur (c) la technique de conception de bases de données logiques, créée par Robert G. Brown.
Party
ligne doit être complétée à tout moment par (ii) la contrepartie respective dans exactement l' un des tableaux représentant les sous-types, qui (iii) doit« respecter »la valeur contenue dans leParty.PartyTypeCode
colonne "indiquant le discriminateur ".