5

Supponiamo che io abbia le seguenti tabelle:SQL: normalizzazione della banca dati, pur mantenendo i vincoli

 ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |OrganismId (int, PK)|   |SpeciesId (int, PK) | 
    |SpeciesId (int, FK) |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |____________________|      1 
       1         | 
       |         | 
       |         | 
       ∞         ∞ 
    ______________________  ____________________   _______________ 
    | OrganismPropsValues |  | SpeciesProps  |  |  Props  | 
    |----------------------|  |--------------------|  |---------------| 
    |OrganismId (int, FK) |  |PropId (int,PK,FK) | ∞-----1|PropId (int,PK)| 
    |PropId (int, FK)  |  |SpeciesId(int,PK,FK)|  |Name (varchar) | 
    |Value (varchar)  |  |____________________|  |_______________| 
    |______________________|            1 
       ∞               | 
       |               | 
       ----------------------------------------------------------- 

Una rapida spiegazione di quello che sto cercando di rappresentare qui: supponiamo di avere un elenco di specie, come gatto, cane , umano, ecc Abbiamo anche un set di proprietà (puntelli abbreviati così ho potuto adattarsi più facilmente nel diagramma) applicabili in alcuni, ma non necessariamente tutte le specie - per esempio, puo essere lunghezza della coda (per le specie con code), il colore degli occhi (per quelli con gli occhi), ecc

SpeciesProps è una tabella linker che definisce quali si applicano le proprietà a cui species-- ecco che wou ld {Human, Eye Color}, {Dog, Eye Color}, {Cat, Eye Color}, {Dog, Tail Length}, {Cat, Tail Length}. Non abbiamo {Human, Tail Length} perché Tail Length non è ovviamente una proprietà valida da applicare a un essere umano.

La tabella Organismi contiene "implementazioni" effettive delle specie-- Quindi qui potremmo avere {Human, Bob}, {Dog, Rufus} e {Cat, Felix}.

Ecco ora il mio problema: nella tabella OrganismPropsValues, voglio memorizzare i "valori" delle proprietà per ciascun organismo - così, ad esempio, per Bob voglio memorizzare {Bob, Eye Color, Blue}. Per Rufus, vorrei memorizzare {Rufus, Eye Color, Brown} e {Rufus, Tail Length, 20} (simile a Felix). Il mio problema però, è che nello schema che ho descritto, è perfettamente possibile memorizzare {Bob, Coda Lunghezza, 10}, anche se il {umana, terminale Lunghezza} tupla non esiste in SpeciesProps. Come posso modificare questo schema in modo da poter applicare i vincoli definiti in SpeciesProps in OrganismPropsValues, pur mantenendo un'adeguata normalizzazione?

+0

A seconda del DB (ad esempio Oracle) Vorrei solo creare un po 'di stored procedure per INSERT/UPDATE/DELETE e realizzare qualsiasi vincoli complessi in là ... – Yahia

+0

@Yahia grazie per il suggerimento, ma se c'è un modo per farlo senza introdurre procedure, trigger, ecc. Preferirei questo. Questo è MS-SQL (2008). – Andrew

+0

Mi ha fatto male alla testa.Questo sarà orribile da interrogare (pensa a quanti join ci vorranno per ottenere tutti i dati su un essere umano!) Ed è un progetto così povero che non so da dove cominciare. I database non sono oggetti e non devono essere progettati come oggetti. Le tabelle EAV sono una soluzione estremamente scadente. Assumi un vero progettista di database. – HLGEM

risposta

4

stai attuazione del Entity-Attribute-Value antipattern. Questo non può essere un progetto di database normalizzato, perché non è relazionale.

Quello che vorrei suggerire invece è il modello di progettazione Class Table Inheritance:

  • Creare una tabella per organismi, contenente le proprietà comuni a tutte le specie.
  • Creare una tabella per specie, contenente proprietà specifiche per tale specie. Ognuna di queste tabelle ha una relazione 1-a-1 con Organismi, ma ogni proprietà appartiene alla sua colonna.

    ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |OrganismId (int, PK)|   |SpeciesId (int, PK) | 
    |SpeciesId (int, FK) |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |____________________| 
          1 
          | 
          | 
          1 
    ______________________ 
    | HumanOrganism  | 
    |----------------------| 
    |OrganismId (int, FK) | 
    |Sex  (enum)  | 
    |Race  (int, FK) | 
    |EyeColor (int, FK) | 
    |....     | 
    |______________________| 
    

Questo vuol dire si creerà molti tavoli, ma considerare questo come un compromesso con i molti vantaggi pratici per la memorizzazione di proprietà in modo relazionale corretto:

  • È possibile utilizzare i dati di SQL digita appropriatamente, invece di considerare tutto un varchar a forma libera.
  • È possibile utilizzare i vincoli o le tabelle di ricerca per limitare determinate proprietà mediante un insieme predefinito di valori.
  • È possibile rendere obbligatorie le proprietà (ad esempio NOT NULL) o utilizzare altri vincoli.
  • I dati e gli indici vengono memorizzati in modo più efficiente.
  • Le query sono più facili da scrivere e più semplici da eseguire per RDBMS.

Per ulteriori informazioni su questo progetto, si veda il libro di Martin Fowler Patterns of Enterprise Application Architecture, o la mia presentazione Practical Object-Oriented Models in SQL, o il mio libro, SQL Antipatterns: Avoiding the Pitfalls of Database Programming.

+0

Grazie per il suggerimento alternativo ... esaminerò questo schema. – Andrew

2

Hmm ...
Ecco un modo per farlo:
Aggiungi SpeciesPropsId nella tabella SpeciesProps.
Sostituire PropID con SpeciesPropsId nella tabella OrganismPropsValues.
Sarà necessario modificare un po 'i vincoli.
necessario aggiungere SpeciesProps a OrganismPropsValues ​​vincolare.
necessario rimuovere OrganismPropsValues ​​a Props vincolare.

Tecnicamente non è necessario rimuovere PropId da OrganismPropsValues, ma se lo si mantiene renderà i dati ridondanti.

1

Un altro modo per raggiungere questi limiti sarebbe quello di modificare il PK della tabella Organism facendo cadere OrganismId e aggiungendo uno No. Quindi rendere PK il composto (SpeciesId, No). Così, "Bob" sarebbe (Human, 1), "Rufus" sarebbe (Dog, 1), ecc

Quindi, aggiungere nella tabella OrganismPropsValues, il SpeciesId e il No (rimuovendo il OrganismId.)

Questo permetterà di cambiare l'FK da OrganismPropsValues a Props fare riferimento SpeciesProps invece:

 ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |SpeciesId (int, FK) |   |SpeciesId (int, PK) | 
    |No (int)   |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |PK (SpeciedId,No) |      1 
    |____________________|      | 
       1         | 
       |         | 
       |         | 
       ∞         ∞ 
    ______________________  ____________________   _______________ 
    | OrganismPropsValues |  | SpeciesProps  |  |  Props  | 
    |----------------------|  |--------------------|  |---------------| 
    |SpeciesId (int, PK) |  |PropId (int,PK,FK) | ∞-----1|PropId (int,PK)| 
    |No (int, PK)   |  |SpeciesId(int,PK,FK)|  |Name (varchar) | 
    |PropId (int, PK)  |  |____________________|  |_______________| 
    |Value (varchar)  |     1 
    |FK (SpeciesId,No)  |     | 
    |FK (SpeciesId,PropId) |     | 
    |______________________|     | 
       ∞        | 
       |        | 
       ------------------------------- 
+0

Cosa si riferisce @HLGEM con il problema "EAV" è il campo "OrganismPropsValues.Value'. Non esiste un modo semplice per avere controlli di integrità su quel campo in quanto può memorizzare diversi tipi di dati. Ad esempio, eviti di memorizzare '{Bob, Tail Length, 10}' con questa struttura db ma non puoi evitare '{Rufus, Tail Length, Blue}' o '{Bob, Eye Color, 20}'. –

+0

Grazie-- Ho già sentito le nozioni di base su EAV, ma non ne ho mai usato uno nella pratica. Questo è davvero un progetto parallelo su cui sto lavorando, non come se volessi inserirlo nel software di produzione, ma i dati che sto cercando di modellare qui sembrano chiederglielo. Ovviamente qualsiasi suggerimento per alternative sono ben accetti - non ho intenzione di usarlo. – Andrew

2

Ogni volta che si dispone di una dipendenza a forma di diamante come questa, considerare di porre maggiore enfasi su composito CHIAVI PRIMARI.

In particolare, individuare l'organismo non solo da OrganismId, ma dalla combinazione di SpeciesId e OrganismSubId (si può ancora avere OrganismId, ma lo mantiene come chiave alternativa - per non mostrare qui per brevità).

Una volta fatto questo, il tuo modello può essere fatto per assomigliare a questo:

ER Model

La cosa fondamentale da notare qui è che SpeciesId si "propaga" giù entrambi i bordi di questo di diamante grafico a forma di Questo è ciò che ti dà la restrizione desiderata di non essere in grado di "assegnare un valore" a una proprietà che non è stata "dichiarata" per la specie data.

BTW, utilizzare singolare quando si denominano le tabelle. Inoltre, considera l'utilizzo di chiavi primarie naturali (ad esempio SpeciesName anziché SpeciesId come PK): se eseguito correttamente, può aumentare notevolmente la velocità dei tuoi JOIN (soprattutto in combinazione con il clustering).