Compromesso tra correttezza formale e convenienza nelle relazioni con il database

3

Supponiamo che tu stia modellando e applichi in cui avrai 4 tipi di entità, ad esempio

  • a Person
  • a Job
  • a Machine
  • a Chemical

Ora devi modellare tutte le possibili relazioni (N: M) tra quelle entità, perché la tua logica aziendale è tale che ha davvero senso, quindi finisci per avere molte tabelle:

  • Person_Job (person_id, job_id)
  • Person_Machine (person_id, machine_id)
  • Person_Chemical (person_id, chemical_id)
  • Job_Machine (job_id, machine_id)
  • Job_Chemical (job_id, chemical_id)
  • Machine_Chemical (machine_id, chemical_id)

Questa è (4 x 3) / 2 = 6 tabelle.

Ora, se interviene un'altra entità, diventerebbero (5 x 4) / 2 = 10 tabelle; e con un altro (6 x 5) / 2 = 15 tabelle, e così via, ogni volta aggiungendo un'altra N - 1 di tabelle.

Sarebbe comunque corretto implementarne solo uno aggiuntivo?

  • Relations (from_entity, from_id, to_entity, to_id)

In questo modo, mi sembra che sia meno corretto da un punto di vista formale, ma con solo due tabelle puoi modellare qualsiasi tipo di relazione nel sistema.

A quale numero di entità e relazioni potrebbe essere preferibile quest'ultima forma prima della prima?

Tieni presente che questo è solo un esempio inventato per chiarire il problema in questione: il dominio reale conterrebbe molte entità, circa 30.

EDIT: Penso che possiamo ancora avere l'integrità della chiave esterna RDBMS con alcune tabelle aggiuntive, ad esempio

EntityType (
    entity_type varchar primary key
)

Entity (
    entity_uuid uuid primary key,
    entity_type varchar references EntityType(entity_type),
)

Relations (
    from_uuid uuid references Entity(entity_uuid),
    to_uuid uuid references Entity(entity_uuid),
    primary key (from_uuid, to_uuid)
)

Job (
    job_uuid references Entity(entity_uuid),
    job_name varchar,
    [...]
)

Person (
    person_uuid references Entity(entity_uuid),
    person_first_name varchar,
    person_last_name varchar,
    [...]
)

In tale scenario, per prima cosa inserisci la riga nella tabella Entity e poi nella tabella specifica; allo stesso modo si farebbero le delezioni sulla tabella Entity e i vari FK potrebbero fare un DELETE CASCATA per mantenere i dati coerenti.

    
posta Matteo Tassinari 02.02.2017 - 20:28
fonte

5 risposte

9

Si prega di non

Teoria

La parola "relazione" (come nel database relazionale) ha un significato molto specifico. Non si tratta di relazioni. È un termine della teoria degli insiemi che definisce quale risultato di una query è - un prodotto incrociato tra tutte le tabelle di interesse, soggetto a un vincolo (una serie di predicati derivati dai join della tabella). Una relazione è un vincolo. Tutta la scienza informatica dietro gli algoritmi del database proviene da proprietà di relazioni e vincoli. Se il tuo modo di pensare differisce da quegli scienziati, stai ignorando la scienza.

In conclusione: i vincoli sono molto importanti.

Ogni tanto un ingegnoso ingegnere di database decide che vuole essere più simile a uno sviluppatore c ++. Vuole che le cose siano riutilizzabili, generiche, simili nel modello. Questo tipo di pensiero dà origine a modelli come EAV dove qualsiasi entità può essere memorizzati in una singola struttura generica. Bene, EAV è un disegno ampiamente odiato e causa tutti i tipi di problemi ... se hai mai usato Magento, per esempio, tutta quella roba EAV distrugge le prestazioni del sistema e l'unico cerotto serve a scaricare un lavoro che genera indici appiattendo l'EAV.

Non essere lui. Abbraccia i vincoli.

I vincoli consentono di ottenere risultati di query. I vincoli rendono possibile l'integrità referenziale. La conoscenza di come funzionano i vincoli rende possibile ogni tipo di ottimizzazione delle prestazioni. Il tuo motore di database è stato creato da scienziati informatici che hanno familiarità con questo corpo di matematica.

Se ti allontani da questa filosofia, inconsapevolmente stai scendendo dalla prenotazione e probabilmente ti troverai in disordine molto prima di averlo realizzato.

Practice

Appena fuori dalla mia testa qui ci sono qualcosa che ti ferirà più tardi se insisti su questo generico modello M: N.

  1. Sarai costretto a utilizzare lo stesso tipo di dati per tutte le chiavi primarie.

  2. Non potrai utilizzare le chiavi naturali, che a volte sono superiori (ad esempio se devi cercare un intervallo di record che potrebbe essere identificato da un intervallo di chiavi naturali).

  3. Sei limitato in termini di proprietà dello spazio dei nomi (ad esempio, se hai scelto un int per il tuo PK, non sarai in grado di utilizzare GUID per le tabelle che contengono righe che potrebbero provenire da origini dati diverse) .

  4. Si verificano problemi con la replica transazionale se esiste più di un editore (le chiavi su ciascun editore richiedono il proprio spazio dei nomi).

  5. Non sarà possibile utilizzare le chiavi composite anche se in alcuni casi sono più appropriate.

  6. Non sarai in grado di applicare alcun R / I, a meno che non utilizzi i trigger, che non funzioneranno altrettanto bene.

  7. Il design non impone naturalmente alcuna relazione diversa da M: M; non c'è alloggio per qualcosa di limitato a 1: 1 o 1: M. Dovrebbe essere rafforzato dal codice dell'applicazione, o forse da un trigger.

  8. Non potrai utilizzare nulla come le eliminazioni a cascata.

  9. Svilupperai enormi hotspot e serrature controverse sul tuo singolo tavolo M: N.

  10. Non sarai in grado di definire relazioni attribuite, cioè se hai bisogno di registrare informazioni sulla relazione tra una persona e una macchina ("può usare", "è proibito", " è certificato "," è un allenatore ") non hai nessun posto dove metterlo e da nessuna parte è sensato aggiungerlo.

P.S.

  1. Un analista di business che esamina il tuo schema per avere un'idea delle regole aziendali avrà un momento difficile. Le relazioni potrebbero essere comprese solo immergendosi nel codice, il che è molto più complicato.
risposta data 02.02.2017 - 23:09
fonte
8

Per prima cosa, vorrei segnalare qualcosa: se ti senti obbligato a stabilire una relazione N: M tra le due permutazioni possibili di due entità, potrebbe significare che non stai studiando bene il problema del dominio. Non ho mai visto un caso in cui hai bisogno di una relazione (N: M o altro) tra qualsiasi entità possibile e qualsiasi altra possibile entità.

Ora vai alle tue tabelle Relazioni (non dovrebbe essere Relazioni?).

  • Non puoi avere due FK sulla stessa colonna che puntano a due o più diverse tabelle "parent".
  • Un FK può solo puntare a una tabella genitore.
  • La tua soluzione richiederebbe l'applicazione di tutto in codice, il che significa che non avrai l'integrità referenziale per l'RDBMS.
  • Dovrai memorizzare il nome dell'entità come stringa, più controllo di integrità basato su codice (incline agli errori).
  • Non è possibile interrogare il database con un generatore di query grafico, se lo si desidera.
  • I dati non saranno di valore al di fuori "dell'app".

Per me quella soluzione è un no-go. La mia raccomandazione è che studi il problema del dominio più duro e scoprirai che non hai bisogno di una relazione per ogni possibile combinazione di due entità.

    
risposta data 02.02.2017 - 20:57
fonte
7

A giudicare dalla particolarità del tuo modello, in cui ogni singola entità ha una relazione con ogni altra entità, e guardando il tuo commento all'eccellente risposta di Tulains Córdova, sono portato a credere che tu abbia un modello eccezionalmente strano nelle tue mani .

Iniziamo da questo: tu dici che il dominio è ben compreso, ma ti preoccupi anche di ciò che accade man mano che continui ad aggiungere entità. Queste due affermazioni sono in disaccordo tra loro: o il tuo dominio è ben compreso, nel qual caso hai già praticamente tutte le entità e le relazioni tra di loro che avresti avuto, oppure no.

Quello che sospetto che stia accadendo è che le tue entità sono altamente isomorfe l'una con l'altra, al punto in cui non sono in realtà entità separate. Appartengono tutti alla stessa entità di base, (chiamiamola "Entità" con la "E" maiuscola), e il fatto che un'entità è una Persona mentre un'altra Entità è una Macchina e così via è solo un attributo dell'Entità. Se questo è il caso, allora la soluzione migliore potrebbe essere quella di avere una singola tabella "Entity" e una singola tabella di relazioni many-to-many in cui qualsiasi entità può avere una relazione con un qualsiasi numero di altre entità nella stessa tabella.

Questo sarebbe un approccio valido per quanto riguarda la teoria dei database relazionali e perfettamente funzionante dal punto di vista dell'integrità referenziale. Funzionerebbe anche molto meglio della tua mostruosa tabella "Relazioni" che unisce tabelle disparate.

Se le tue entità non sono interamente isomorfe l'una con l'altra, quindi devono avere colonne diverse ciascuna (in realtà?), allora puoi utilizzare l'ereditarietà come fanno gli ORM. Per i dettagli su questo puoi leggere su come hibernate implementi l'ereditarietà (guarda la strategia "table per class", salta la strategia "table per hierarchy" e la nozione zoppa di "discriminator column",) ma per darti una veloce idea di cosa si tratta, il tuo schema potrebbe apparire come questo:

Tabella Entità

    entity_id, entity_column2, entity_column3... person_entity_id, machine_entity_id

Tabella ManyEntitiesToManyEntities

    left_entity_id, right_entity_id

Tabella PersonEntity

    entity_id, person_column2, person_column3...

Tabella MachineEntity

    entity_id, machine_column2, machine_column3...

... e così via.

Quindi, se dovessi aggiungere una persona al tuo database, inizieresti a pubblicare un id (preferibilmente usando una sequenza, per salvare un roundtrip nel database) diciamo 10, e poi aggiungerei una nuova riga a la tabella Entity, dove person_entity_id sarebbe 10 e machine_entity_id sarebbe NULL, poiché questa riga di entità corrisponde a una persona, non a una macchina. Quindi, aggiungerai anche una riga alla tabella delle persone, con un entity_id di 10 anche.

Una cosa è chiara: la tabella "Relazioni" suggerita dalla tua domanda è un no-go perché distrugge completamente il controllo dell'integrità referenziale.

    
risposta data 02.02.2017 - 21:27
fonte
5

Il tuo esempio è IMHO irrealistico, poiché se il modello riflette qualsiasi tipo di dati del mondo reale, non ha senso modellare esplicitamente ogni relazione M: N possibile. Mentre in un piccolo modello con 4 tabelle questo potrebbe accadere, in un modello più grande con 30 tavoli è molto improbabile che questo approccio abbia un senso.

I modelli relazionali ben progettati dovrebbero cercare di ottenere il minor numero possibile di ridondanze (tranne in rari casi, per problemi di ottimizzazione). Quindi, se una relazione M: N è solo una conseguenza di un'altra relazione (come "la persona X è correlata alla macchina Y perché c'è una relazione persona-lavoro e una relazione lavoro-macchina), dovresti evitare di modellare la relazione ridondante non necessaria tra persone e macchine Naturalmente, potrebbe esserci qualche volta una relazione tra macchina e persona che non è ridondante ai primi due, e quindi potrebbe essere logico modellarla esplicitamente, ma per mia esperienza è estremamente improbabile che questa sia la situazione tra ogni coppia delle tue 30 tabelle.

Tuttavia, per giudicare se una relazione è ridondante, è necessario conoscere il significato effettivo del dominio. Ad esempio, una relazione Person_Job è pensata per esprimere un significato aziendale, potrebbe essere una persona

  • è assegnato a un lavoro (forse per contratto)

  • è qualificato per il lavoro (ma non è necessariamente assegnato ad esso)

  • si applica per il lavoro

Potresti anche voler modellare tutti questi significati, il che potrebbe finire con l'avere più di una tabella di link tra la stessa coppia di tabelle - cosa che IMHO andrebbe bene finché il tuo sistema contiene casi d'uso per tutti questi tipi diversi delle relazioni.

Secondo la mia esperienza, quando limiti le tue tabelle di link a quelle che effettivamente hai bisogno di modellare i casi d'uso nel tuo sistema, e evita di modellare qualsiasi tabelle di collegamento ridondanti , non ottieni un'esplosione combinatoria di tabelle.

Assegnare un nome alle tue tabelle di link come Person_IsQualifiedTo_Job esprime il significato e ti dà un modello che è facile da capire. Un nome come Person_Job non riflette questo ed è più difficile capire che tipo di relazione si intende. E l'utilizzo di una tabella generica Relations nasconderà completamente il significato delle relazioni, quindi sarà difficile mantenerlo ed estenderlo, anche se a prima vista ti sembrerà conveniente.

    
risposta data 02.02.2017 - 22:02
fonte
0

Potresti prendere in considerazione le relazioni tra entità . Avrai flessibilità (presumendo che sia l'intero scopo), ma le prestazioni ne risentiranno.

Penso che questo requisito sia più di un piacere avere invece di un must. Personalmente, scaverei più a fondo e spingere indietro un po 'per vedere come funziona nel mondo reale.

    
risposta data 02.02.2017 - 20:54
fonte

Leggi altre domande sui tag