Se comprendo correttamente le vostre specifiche, il vostro scenario implica - tra gli altri aspetti significativi - una struttura di sottotipi di sottotipi .
Di seguito illustrerò come (1) modellarlo a livello concettuale di astrazione e successivamente (2) rappresentarlo in un progetto DDL a livello logico .
Regole di business
Le seguenti formulazioni concettuali sono tra le regole più importanti nel tuo contesto aziendale:
- Una playlist è di proprietà di esattamente un gruppo o esattamente un utente in un determinato momento
- Una playlist può essere di proprietà di uno o più gruppi o utenti in momenti distinti nel tempo
- Un utente possiede playlist a zero-uno-o-molte
- Un gruppo possiede playlist pari a zero o una o più
- Un gruppo è composto da uno-a-molti membri (che devono essere utenti )
- Un utente può essere un membro di zero-uno-o-molti gruppi .
- Un gruppo è composto da uno-a-molti membri (che devono essere utenti )
Poiché le associazioni o relazioni (a) tra Utente e Playlist e (b) tra Gruppo e Playlist sono abbastanza simili, questo fatto rivela che Utente e Gruppo sono sottotipi di entità reciprocamente esclusivi del Partito 1 , che a sua volta è il loro sottotipo di entità —supertype- i cluster di sottotipi sono strutture di dati classiche che nascono in schemi concettuali di tipi molto diversi—. In questo modo, si possono affermare due nuove regole:
- Una parte è classificata per esattamente un PartyType
- Una parte è un gruppo o un utente
E quattro delle regole precedenti devono essere riformulate come solo tre:
- Una playlist è di proprietà di esattamente una parte in un determinato momento
- Una playlist può essere di proprietà di una o più parti in momenti distinti nel tempo
- Una festa possiede playlist a zero-uno-o-molte
Diagramma IDEF1X dell'esposizione
Il diagramma IDEF1X 2 mostrato in Figura 1 consolida tutte le suddette regole aziendali insieme ad altre che sembrano pertinenti:
Come dimostrato, Gruppo e Utente sono rappresentati come sottotipi collegati dalle rispettive linee e dal simbolo esclusivo con Party , il supertipo.
La proprietà Party.PartyTypeCode sta per discriminatore dei sottotipi , ovvero indica quale tipo di istanza di sottotipo deve integrare una determinata occorrenza di supertipo.
Inoltre, Party è collegato con Playlist tramite la proprietà OwnerId che è raffigurata come CHIAVE ESTERA che punta a Party.PartyId . In questo modo, la Parte interconnette (a) Playlist con (b) Gruppo e (c) Utente .
Di conseguenza, poiché una particolare istanza di una parte è un gruppo o un utente , una playlist specifica può essere collegata al massimo con un occorrenza di sottotipo.
Layout illustrativo a livello logico
Il diagramma IDEF1X precedentemente esposto mi ha servito come piattaforma per creare la seguente disposizione logica SQL-DDL (e ho fornito note come commenti che evidenziano diversi punti di particolare rilevanza —eg, le dichiarazioni di vincolo—):
-- 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)
);
Naturalmente, è possibile apportare una o più regolazioni in modo che tutte le caratteristiche del contesto aziendale siano rappresentate con la precisione necessaria nel database effettivo.
Nota : ho testato il layout logico sopra riportato su questo violino db <> e anche su questo violino SQL , entrambi "in esecuzione" su PostgreSQL 9.6, in modo da poterli vedere "in azione".
The Slugs
Come puoi vedere, non ho incluso Group.Slug
né Playlist.Slug
come colonne nelle dichiarazioni DDL. Questo perché, in accordo con la tua seguente spiegazione
Si slug
tratta di versioni univoche, minuscole e trattate delle rispettive entità title
. Ad esempio, a group
con il title
"Gruppo test" avrebbe il slug
"gruppo test". I duplicati vengono aggiunti con numeri interi incrementali. Questo cambierebbe ogni volta che i loro title
cambiamenti. Credo che ciò significhi che non sarebbero grandi chiavi primarie? Sì, slugs
e usernames
sono unici nelle rispettive tabelle.
si può concludere che i loro valori sono derivabili (cioè, devono essere calcolati o calcolati in termini di corrispondenti Group.Title
e Playlist.Title
valori, a volte in combinazione con — presumo, una sorta di INTEGER generati dal sistema), quindi non dichiarerei dette colonne in una delle tabelle di base in quanto introdurrebbero irregolarità di aggiornamento.
Al contrario, produrrei il Slugs
forse, in una vista , che (a) include la derivazione di tali valori in colonne virtuali e (b) può essere usato direttamente in ulteriori operazioni SELECT - si potrebbe ottenere l'aggiunta della parte INTEGER, ad esempio combinando il valore di (1) il Playlist.OwnerId
con (2) i trattini intermedi e (3) il valore di Playlist.Title
;
o, in virtù del codice del programma applicativo, imitando l'approccio descritto in precedenza (forse proceduralmente), una volta recuperati e formattati i set di dati pertinenti per l'interpretazione dell'utente finale.
In questo modo, qualsiasi di questi due metodi eviterebbe il meccanismo di “sincronizzazione aggiornamento” che dovrebbe essere messo in atto sse la Slugs
si mantengono in colonne di tabelle di base.
Considerazioni di integrità e coerenza
È fondamentale menzionare che (i) ogni Party
riga deve essere integrata in ogni momento da (ii) la rispettiva controparte esattamente in una delle tabelle che rappresentano i sottotipi, che (iii) devono "rispettare" il valore contenuto nella Party.PartyTypeCode
colonna —Denotando il discriminatore—.
Sarebbe abbastanza vantaggioso applicare questo tipo di situazione in modo dichiarativo , ma nessuno dei principali sistemi di gestione di database SQL (incluso Postgres) ha fornito gli strumenti necessari per procedere in questo modo; pertanto, la scrittura di codice procedurale in ACID TRANSACTIONS è finora l'opzione migliore per garantire che le circostanze precedentemente descritte siano sempre soddisfatte nel database. Altre possibilità sarebbero ricorrere ai TRIGGER, ma sono inclini a mettere in ordine le cose, per così dire.
Casi comparabili
Se vuoi stabilire alcune analogie, potresti essere interessato a dare un'occhiata alle mie risposte alle (nuove) domande intitolate
poiché vengono discussi scenari comparabili.
Note finali
1 Parte è un termine usato in contesti legali quando si fa riferimento a un individuo o un gruppo di individui che compongono una singola entità , quindi questa denominazione è adatta a rappresentare i concetti di Utente e Gruppo rispetto all'ambiente aziendale in questione.
2 Integration Definition for Information Modeling ( IDEF1X ) è una tecnica di modellazione dei dati altamente raccomandabile che è stata stabilita come standard nel dicembre 1993 dal National Institute of Standards and Technology (NIST)degli Stati Uniti. È solidamente basato su (a) alcune delle prime opere teoriche create dall'unico ideatore del modello relazionale , ovvero il Dr. EF Codd ; su (b) il punto di vista entità-relazione , sviluppato dal Dr. PP Chen ; e anche su (c) la tecnica di progettazione del database logico, creata da Robert G. Brown.