Come si modella in modo efficace l’ereditarietà in un database?

Quali sono le migliori pratiche per modellare l’ereditarietà nei database?

Quali sono i trade-off (ad esempio la queriability)?

(Sono più interessato a SQL Server e .NET, ma voglio anche capire in che modo altre piattaforms risolvono questo problema).

Esistono diversi modi per modellare l’ereditarietà in un database. Quale scegli dipende dalle tue esigenze. Ecco alcune opzioni:

Tabella per tipo (TPT)

Ogni class ha il suo tavolo. La class base contiene tutti gli elementi della class base, e ogni class che ne deriva ha una propria tabella, con una chiave primaria che è anche una chiave esterna per la tabella della class base; la class della tabella derivata contiene solo i diversi elementi.

Quindi per esempio:

class Person { public int ID; public string FirstName; public string LastName; } class Employee : Person { public DateTime StartDate; } 

Provocherebbe tabelle come:

 table Person ------------ int id (PK) string firstname string lastname table Employee -------------- int id (PK, FK) datetime startdate 

Tabella per gerarchia (TPH)

C’è una singola tabella che rappresenta tutta la gerarchia dell’eredità, il che significa che molte delle colonne probabilmente saranno sparse. Viene aggiunta una colonna discriminatore che indica al sistema quale tipo di riga è.

Date le classi sopra, finisci con questa tabella:

 table Person ------------ int id (PK) int rowtype (0 = "Person", 1 = "Employee") string firstname string lastname datetime startdate 

Per tutte le righe che sono rowtype 0 (Person), la data di inizio sarà sempre nullo.

Tabella per calcestruzzo (TPC)

Ogni class ha il suo tavolo completamente formato senza riferimenti a nessun altro tavolo.

Date le classi sopra, finisci con queste tabelle:

 table Person ------------ int id (PK) string firstname string lastname table Employee -------------- int id (PK) string firstname string lastname datetime startdate 

La corretta progettazione del database non assomiglia alla corretta progettazione degli oggetti.

Se si prevede di utilizzare il database per scopi diversi dalla semplice serializzazione degli oggetti (ad esempio report, query, utilizzo di più applicazioni, business intelligence, ecc.), Non è consigliabile alcun tipo di mapping semplice dagli oggetti alle tabelle.

Molte persone pensano a una riga in una tabella di database come quadro (ho passato molti anni a pensare in questi termini), ma una riga non è un’entity framework. È una proposizione. Una relazione tra database (ad esempio, tabella) rappresenta alcune affermazioni di fatto sul mondo. La presenza della riga indica che il fatto è vero (e al contrario, la sua assenza indica che il fatto è falso).

Con questa comprensione, puoi vedere che un singolo tipo in un programma orientato agli oggetti può essere memorizzato attraverso una dozzina di relazioni diverse. E una varietà di tipi (uniti per ereditarietà, associazione, aggregazione o completamente non affiliati) possono essere parzialmente memorizzati in una singola relazione.

È meglio chiedere a te stesso, quali dati vuoi archiviare, a quali domande vorresti rispondere, quali rapporti vuoi generare.

Una volta creata la progettazione DB appropriata, è semplice creare query / viste che consentano di serializzare gli oggetti in tali relazioni.

Esempio:

In un sistema di prenotazione alberghiera, potrebbe essere necessario memorizzare il fatto che Jane Doe abbia prenotato per una camera al Seaview Inn per il 10-12 aprile. È un attributo dell’entity framework cliente? È un attributo dell’ quadro dell’hotel? È un’ quadro di prenotazione con proprietà che includono clienti e hotel? Potrebbe essere qualsiasi o tutte quelle cose in un sistema orientato agli oggetti. In un database, non è nessuna di quelle cose. È semplicemente un fatto.

Per vedere la differenza, considera le seguenti due query. (1) Quante prenotazioni alberghiere ha Jane Doe per il prossimo anno? (2) Quante camere sono prenotate per il 10 aprile al Seaview Inn?

In un sistema orientato agli oggetti, query (1) è un attributo dell’ quadro cliente e query (2) è un attributo dell’entity framework dell’hotel. Quelli sono gli oggetti che espongono tali proprietà nelle loro API. (Sebbene, ovviamente, i meccanismi interni con cui tali valori sono ottenuti possono implicare riferimenti ad altri oggetti.)

In un sistema di database relazionale, entrambe le query esaminerebbero la relazione di prenotazione per ottenere i loro numeri e concettualmente non è necessario preoccuparsi di altre ” quadro”.

Pertanto, tentando di memorizzare fatti sul mondo, piuttosto che tentare di archiviare quadro con attributi, si costruisce un database relazionale adeguato. E una volta che è stato progettato correttamente, allora le query utili che non si sono avverate durante la fase di progettazione possono essere facilmente costruite, dal momento che tutti i fatti necessari per soddisfare tali domande sono nei loro posti appropriati.

Risposta breve: non lo fai.

Se hai bisogno di serializzare i tuoi oggetti, usa un ORM, o meglio qualcosa come activerecord o prevarence.

Se è necessario memorizzare i dati, memorizzarli in modo relazionale (prestando attenzione a ciò che si sta archiviando e prestando attenzione a ciò che Jeffrey L Whitledge ha appena detto), non uno influenzato dal design dell’object.

Esistono due tipi principali di ereditarietà che è ansible impostare in un DB, tabella per quadro e tabella per gerarchia.

Tabella per quadro è dove hai una tabella di entity framework di base che ha proprietà condivise di tutte le classi figlie. Quindi hai una class per ogni altra tabella ciascuno con solo proprietà applicabili a quella class. Sono collegati 1: 1 dai loro PK

alt text http://sofit.miximages.com/.net/ent.jpg

La tabella per gerarchia è dove tutte le classi hanno condiviso una tabella e le proprietà facoltative sono annullabili. Il loro è anche un campo discriminatore che è un numero che indica il tipo attualmente detenuto dal record

alt text http://sofit.miximages.com/.net/hier.jpg SessionTypeID è discriminatore

La destinazione per gerarchia è più veloce per la query poiché non è necessario il join (solo il valore del discriminatore), mentre il target per quadro è necessario per creare join complessi al fine di rilevare il tipo di qualcosa e restituire tutti i dati.

Modifica: Le immagini che mostro qui sono schermate di un progetto su cui sto lavorando. L’immagine Asset non è completa, quindi il suo vuoto, ma era principalmente per mostrare come la sua configurazione, non cosa mettere dentro i tuoi tavoli. Questo dipende da te ;). La tabella della sessione contiene informazioni sulla sessione di collaborazione virtuale e può essere di diversi tipi di sessioni a seconda del tipo di collaborazione coinvolta.

I modelli TPT, TPH e TPC sono i modi in cui vai, come menzionato da Brad Wilson. Ma un paio di note:

  • le classi figlie che ereditano da una class base possono essere viste come quadro deboli alla definizione della class base nel database, nel senso che dipendono dalla loro class base e non possono esistere senza di essa. Ho visto il numero di volte in cui gli ID univoci vengono archiviati per ciascuna tabella figlio mantenendo anche l’FK nella tabella padre. Un FK è appena sufficiente ed è ancora meglio avere on-delete abilitazione a cascata per la relazione FK tra il bambino e le tabelle di base.

  • In TPT, vedendo solo i record della tabella di base, non si è in grado di trovare la class figlio rappresentata dal record. Talvolta ciò è necessario quando si desidera caricare un elenco di tutti i record (senza eseguire select su ogni tabella figlio). Un modo per gestirlo è quello di avere una colonna che rappresenti il ​​tipo della class figlio (simile al campo rowType nel TPH), quindi mescolare in qualche modo TPT e TPH.

Supponiamo di voler progettare un database che contenga il seguente diagramma di classi di forma:

 public class Shape { int id; Color color; Thickness thickness; //other fields } public class Rectangle : Shape { Point topLeft; Point bottomRight; } public class Circle : Shape { Point center; int radius; } 

La progettazione del database per le classi precedenti può essere così:

 table Shape ----------- int id; (PK) int color; int thichkness; int rowType; (0 = Rectangle, 1 = Circle, 2 = ...) table Rectangle ---------- int ShapeID; (FK on delete cascade) int topLeftX; int topLeftY; int bottomRightX; int bottomRightY; table Circle ---------- int ShapeID; (FK on delete cascade) int centerX; int center; int radius; 

Si normalizzerebbe il proprio database e questo rispecchierebbe in realtà l’ereditarietà. Potrebbe avere un degrado delle prestazioni, ma è così con la normalizzazione. Probabilmente dovrai usare il buon senso per trovare l’equilibrio.

ripetere la risposta thread simile

in OR mapping, le mappe di ereditarietà in una tabella padre in cui le tabelle padre e figlio utilizzano lo stesso identificatore

per esempio

 create table Object ( Id int NOT NULL --primary key, auto-increment Name varchar(32) ) create table SubObject ( Id int NOT NULL --primary key and also foreign key to Object Description varchar(32) ) 

SubObject ha una relazione di chiave esterna con Oggetto. quando crei una riga SubObject, devi prima creare una riga Oggetto e utilizzare l’Id in entrambe le righe

EDIT: se stai cercando di modellare anche il comportamento, avresti bisogno di una tabella Type che elencasse le relazioni di ereditarietà tra le tabelle e specificasse l’assembly e il nome della class che implementava il comportamento di ogni tabella

sembra eccessivo, ma tutto dipende da cosa vuoi usarlo!

Usando SQL ALchemy (Python ORM), puoi fare due tipi di ereditarietà.

Quello che ho avuto esperienza sta usando un tavolo unico e una colonna discriminante. Per le istanze, un database Sheep (senza joke!) Memorizzava tutte le pecore nella stessa tabella e Rams and Ewes venivano gestiti utilizzando una colonna di genere in quella tabella.

Pertanto, puoi richiedere tutte le pecore e ottenere tutte le pecore. Oppure puoi interrogare solo tramite Ram, e otterrà solo Rams. Puoi anche fare cose come avere una relazione che può essere solo un Ariete (cioè il Sire di una Pecora), e così via.

Si noti che alcuni motori di database forniscono già meccanismi di ereditarietà come Postgres . Guarda la documentazione .

Per un esempio, dovresti interrogare il sistema Persona / Dipendente descritto in una risposta sopra in questo modo:

   / * Questo mostra il nome di tutte le persone o dipendenti * /
   SELECT firstname FROM Person; 

   / * Mostra la data di inizio solo per tutti i dipendenti * /
   SELECT startdate FROM Employee;

In questo è la scelta del tuo database, non è necessario essere particolarmente intelligenti!