Hai almeno queste cinque opzioni per modellare la gerarchia dei tipi che descrivi:
Eredità tabella singola : una tabella per tutti i tipi di prodotto, con colonne sufficienti per memorizzare tutti gli attributi di tutti i tipi. Ciò significa molte colonne, la maggior parte delle quali sono NULL su una determinata riga.
Ereditarietà delle classi : una tabella per i prodotti, che memorizza gli attributi comuni a tutti i tipi di prodotti. Quindi una tabella per tipo di prodotto, memorizzando gli attributi specifici per quel tipo di prodotto.
Ereditarietà concreta della tabella : nessuna tabella per gli attributi comuni dei prodotti. Invece, una tabella per tipo di prodotto, che memorizza sia gli attributi comuni del prodotto, sia gli attributi specifici del prodotto.
LOB serializzato : una tabella per i prodotti, che memorizza gli attributi comuni a tutti i tipi di prodotto. Una colonna aggiuntiva memorizza un BLOB di dati semi-strutturati, in XML, YAML, JSON o in qualche altro formato. Questo BLOB consente di memorizzare gli attributi specifici per ciascun tipo di prodotto. È possibile utilizzare modelli di design fantasiosi per descriverlo, come Facciata e Memento. Tuttavia, indipendentemente dal fatto che tu abbia una serie di attributi che non possono essere facilmente interrogati in SQL; devi recuperare l'intero BLOB indietro nell'applicazione e ordinarlo là fuori.
Entity-Attribute-Value : una tabella per i prodotti e una tabella che ruota gli attributi sulle righe anziché sulle colonne. L'EAV non è un progetto valido rispetto al paradigma relazionale, ma molte persone lo usano comunque. Questo è il "Modello di proprietà" menzionato da un'altra risposta. Vedi altre domande con il tag eav su StackOverflow per alcune insidie.
Ho scritto di più su questo in una presentazione, Extensible Data Modeling .
Ulteriori pensieri sull'EAV: anche se molte persone sembrano favorire l'EAV, io no. Sembra la soluzione più flessibile e quindi la migliore. Tuttavia, tieni presente l'adagio TANSTAAFL . Ecco alcuni degli svantaggi di EAV:
- Non è possibile rendere obbligatoria una colonna (equivalente di
NOT NULL
).
- Non è possibile utilizzare tipi di dati SQL per convalidare le voci.
- Nessun modo per garantire che i nomi degli attributi siano scritti in modo coerente.
- Non è possibile inserire una chiave esterna sui valori di un dato attributo, ad esempio per una tabella di ricerca.
- Il recupero dei risultati in un layout tabellare convenzionale è complesso e costoso, perché per ottenere gli attributi da più righe è necessario fare
JOIN
per ogni attributo.
Il grado di flessibilità che EAV ti offre richiede sacrifici in altre aree, probabilmente rendendo il tuo codice più complesso (o peggio) di quanto sarebbe stato risolvere il problema originale in un modo più convenzionale.
E nella maggior parte dei casi, non è necessario avere quel grado di flessibilità. Nella domanda del PO sui tipi di prodotto, è molto più semplice creare una tabella per tipo di prodotto per attributi specifici del prodotto, quindi è necessario applicare una struttura coerente almeno per le voci dello stesso tipo di prodotto.
Utilizzerei EAV solo se ogni riga deve avere potenzialmente un set distinto di attributi. Quando si dispone di un set finito di tipi di prodotto, EAV è eccessivo. L'ereditarietà delle classi sarebbe la mia prima scelta.
Aggiornamento 2019: più vedo le persone che utilizzano JSON come soluzione per il problema "molti attributi personalizzati", meno mi piace quella soluzione. Rende le query troppo complesse, anche quando si utilizzano funzioni JSON speciali per supportarle. Ci vuole molto più spazio di archiviazione per archiviare documenti JSON, rispetto alla memorizzazione in righe e colonne normali.
Fondamentalmente, nessuna di queste soluzioni è facile o efficiente in un database relazionale. L'intera idea di avere "attributi variabili" è fondamentalmente in contrasto con la teoria relazionale.
Ciò che si riduce è che devi scegliere una delle soluzioni in base alla quale è la meno dannosa per la tua app. Pertanto, è necessario sapere come eseguire la query dei dati prima di scegliere un progetto di database. Non c'è modo di scegliere una soluzione "migliore" perché una qualsiasi delle soluzioni potrebbe essere la migliore per una determinata applicazione.