Schema di database per entità con due possibili tipi proprietario / genitore?


8

Sto usando PostgreSQL con Sequelize come mio ORM.

Ho un tipo User,. Il secondo tipo è Group, che può avere un numero qualsiasi di utenti associati tramite una GroupMembershipstabella. Users può anche possedere un numero qualsiasi di Groups.

Il mio terzo tipo, Playlistpuò appartenere a un UserOR a group. Quale sarebbe il modo migliore per progettare uno schema per questo tipo in modo che possa avere un tipo di proprietario o uno dei due?

Il mio primo passaggio ho creato entrambe le associazioni, ma ho popolato solo una alla volta. Questo potrebbe funzionare, ma sembra confuso e rende difficili le domande.

Informazioni aggiuntive

Ecco le mie risposte alle richieste di chiarimento inviate da MDCCL tramite commenti:

(1) Se una playlist è di proprietà di un determinato gruppo , si può dire che questa playlist è correlata a uno-a-molti utenti , purché siano membri di tale gruppo , giusto?

Credo che questo sia tecnicamente vero, ma questa associazione uno-a-molti non esiste esplicitamente.

(2) Quindi, è possibile che una playlist specifica sia di proprietà di uno-a-molti gruppi contemporaneamente?

No, non dovrebbe essere possibile che uno Playlistsia di proprietà di uno-a-molti Groups.

(3) È possibile che una particolare playlist sia di proprietà di uno-a-molti gruppi e, allo stesso tempo, di uno-a-molti utenti che non sono membri di tale gruppo ?

No, perché come in (2) uno-a-molti da Playlista Groupnon dovrebbe esistere. Inoltre, se a Playlistè di proprietà di a Groupnon è di proprietà di a Usere viceversa. Un solo proprietario alla volta.

(4) Quali sono le proprietà utilizzate per identificare in modo univoco un gruppo , un utente e una playlist ?

Ognuno di essi ha una chiave primaria surrogata ( id), nonché una chiave naturale (sebbene non primaria). Questi sono slugper Groupe Playlist, e usernameper User.

(5) Una particolare playlist potrebbe subire una modifica del proprietario ?

Sebbene non preveda che questa sia una funzionalità (almeno inizialmente), suppongo che ciò possa verificarsi ipoteticamente.

(6) Qual è il significato degli attributi Group.Slug e Playlist.Slug ? I loro valori sono abbastanza stabili per essere definiti come chiavi primarie o cambiano molto spesso? I valori di queste due proprietà, insieme a User.Username devono essere univoci, corretti?

Si slugtratta di versioni univoche, minuscole e trattate delle rispettive entità title. Ad esempio, a groupcon il title"Gruppo test" avrebbe il slug"gruppo test". I duplicati vengono aggiunti con numeri interi incrementali. Questo cambierebbe ogni volta che i loro titlecambiamenti. Credo che ciò significhi che non sarebbero grandi chiavi primarie? Sì, slugse usernamessono unici nelle rispettive tabelle.

Risposte:


9

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:

Figura 1 - Diagramma IDEF1X dei proprietari di playlist

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.SlugPlaylist.Slugcome colonne nelle dichiarazioni DDL. Questo perché, in accordo con la tua seguente spiegazione

Si slugtratta di versioni univoche, minuscole e trattate delle rispettive entità title. Ad esempio, a groupcon il title"Gruppo test" avrebbe il slug"gruppo test". I duplicati vengono aggiunti con numeri interi incrementali. Questo cambierebbe ogni volta che i loro titlecambiamenti. Credo che ciò significhi che non sarebbero grandi chiavi primarie? Sì, slugse usernamessono unici nelle rispettive tabelle.

si può concludere che i loro valori sono derivabili (cioè, devono essere calcolati o calcolati in termini di corrispondenti Group.Titlee Playlist.Titlevalori, 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.OwnerIdcon (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 Slugssi 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.PartyTypeCodecolonna —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.


non vedo alcun vincolo per la festa è esattamente uno di utenti o gruppi
Jasen

Sembra che il tuo commento abbia a che fare con i seguenti estratti di domande (entrambi contenuti nella sezione intitolata "Considerazioni sull'integrità e la coerenza"):
MDCCL

" È fondamentale menzionare che (i) ogni Partyriga 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.PartyTypeCodecolonna —denota il discriminatore— ”.
MDCCL

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 ”.
MDCCL

Pertanto, una volta (si spera presto) Postgres fornisce, diciamo, ASSERZIONI, sarei felice di includere i vincoli (dichiarativi) applicabili. Per il momento, i due approcci disponibili forniti da DBMS sono suggeriti sopra.
MDCCL
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.