Qual è la migliore pratica per questo problema (proprietà diverse per le diverse categorie)?
-
03-07-2019 - |
Domanda
Ho alcuni prodotti che appartiene a qualche categoria.
Ogni categoria può avere diverse proprietà.
Per esempio,
- categoria auto ha proprietà colore, potenza, ...
- categoria animali domestici hanno proprietà peso, età, ...
Numero di categorie è di circa 10-15.Il numero di immobili di ogni categoria è 3-15.Numero di prodotti è molto grande.
Requisito principale di questa app è molto buona ricerca.Possiamo selezionare una categoria e inserire i criteri per ogni struttura di questa categoria.
Sono per la progettazione di database per questo scenario.(SQL Server 2005)
Soluzione
L'approccio di design classico sarebbe (la stella indica la colonna chiave primaria):
Product
ProductId*
CategoryId: FK to Category.CategroyId
Name
Category
CategoryId*
Name
Property
PropertyId*
Name
Type
CategoryProperty
CategoryId*: FK to Category.CategoryId
PropertyId*: FK to Property.PropertyId
ProductProperty
ProductId*: FK to Product.ProductId
PropertyId*: FK to Property.PropertyId
ValueAsString
Se puoi convivere con il fatto che ogni valore di proprietà andrà al DB come una stringa e tipo di informazioni di conversione sono memorizzate nella tabella Proprietà, questo layout sarebbe sufficiente.
La query dovrebbe andare in questo modo:
SELECT
Product.ProductId,
Product.Name AS ProductName,
Category.CategoryId,
Category.Name AS CategoryName,
Property.PropertyId,
Property.Name AS PropertyName,
Property.Type AS PropertyType,
ProductProperty.ValueAsString
FROM
Product
INNER JOIN Category ON Category.CategoryId = Product.CategoryId
INENR JOIN CategoryProperty ON CategoryProperty.CategoryId = Category.CategoryId
INNER JOIN Property ON Property.PropertyId = CategoryProperty.PropertyId
INNER JOIN ProductProperty ON ProductProperty.PropertyId = Property.PropertyId
AND ProductProperty.ProductId = Product.ProductId
WHERE
Product.ProductId = 1
Più condizioni WHERE vengono fornite (congiuntamente, ad es. utilizzando AND), più veloce sarà la query. Se hai indicizzato correttamente le tue tabelle, cioè.
Allo stato attuale, la soluzione non è ideale per una situazione di indicizzazione del testo completo. Una tabella aggiuntiva che memorizza tutto il testo associato a un ProductId in un modo più denormalizzato potrebbe aiutare qui. Questa tabella dovrebbe essere aggiornata tramite i trigger che sono in attesa di modifiche nella tabella ProductProperty.
Altri suggerimenti
Se l'utente dell'applicazione deve selezionare una categoria prima di poter effettuare una ricerca, separerei i prodotti in tabelle di database diverse per categoria. Questa soluzione è anche indicata dal fatto che le categorie stesse hanno così poco in comune. La suddivisione per categoria renderà inoltre ogni ricerca molto più veloce, poiché il tempo non sarà sprecato nella ricerca di auto quando l'utente cerca un animale domestico.
Dopo aver suddiviso i prodotti in categorie, dovrebbe essere facile creare le tabelle utilizzando le proprietà comuni dei prodotti in ciascuna categoria. L'interfaccia utente dell'applicazione dovrebbe essere dinamica (sto pensando a un modulo Web), in quanto le proprietà tra cui l'utente può scegliere dovrebbero cambiare quando l'utente seleziona una categoria.
Nota: se hai prodotti che desideri siano elencati in più categorie, questa soluzione genererà dati duplicati nelle tue tabelle. C'è un compromesso tra velocità e normalizzazione durante la progettazione di un database. Se non hai prodotti che rientrano in più categorie, penso che questa sarà la soluzione più veloce (in termini di velocità di ricerca).
La maggior parte delle persone consiglia di utilizzare varianti del progetto Entity-Attribute-Value (EAV). Questo design è eccessivo per la tua situazione e introduce un sacco di problemi, ad esempio:
- Non è possibile definire il tipo di dati per un attributo; puoi inserire " banana " per un attributo intero
- Non è possibile dichiarare obbligatorio un attributo (ovvero NON NULL in una tabella convenzionale)
- Non puoi dichiarare un vincolo di chiave esterna su un attributo
Se hai un numero limitato di categorie, è meglio usare la soluzione A nella risposta di Bogdan Maxim. Ossia, definire una tabella Prodotti con attributi comuni a tutte le categorie e una tabella aggiuntiva per ogni categoria, per memorizzare gli attributi specifici della categoria.
Solo se hai un numero infinito di categorie o se devi potenzialmente supportare un diverso set di attributi per riga in Prodotti, EAV è una buona soluzione. Ma poi non stai usando affatto un database relazionale, poiché EAV viola diverse regole di normalizzazione.
Se hai davvero bisogno di tanta flessibilità, sarebbe meglio archiviare i tuoi dati in XML. In effetti, potresti esaminare RDF e framework web semantici come Sesame .
Potresti prendere in considerazione un Entity-Attribute-Value di disposizione, dove puoi " tag " ogni prodotto con coppie arbitrarie di nome / valore.
Puoi provare questo. Non sono troppo sicuro dei dettagli reali della tua domanda, forse qualcuno può aiutarti a tradurre un po 'meglio.
5 tabelle. 3 per la memorizzazione dei dati, 2 per la memorizzazione dei mapping tra i dati.
tProduct
productID
<other product details>
tCategory
categoryID
<other category details>
tProperty
propertyID
<other property details>
tProductXCategory
productyID
categoryID
tCategoryXProperty
categoryID
propertyID
Le tue query dovranno unire i dati utilizzando le tabelle di mappatura, ma ciò ti consentirà di avere diverse relazioni diverse tra categoria, proprietà e prodotti.
Utilizza le procedure memorizzate o le query con parametri per ottenere prestazioni migliori dalle tue ricerche.
Se vuoi essere flessibile su categorie e proprietà, devi creare le seguenti tabelle:
- prodotto: ProductID
- categoria: ID categoria, ID prodotto
- proprietà: PropertyID, CategoryID
quando si desidera condividere una categoria su più di un prodotto, è necessario creare una tabella di collegamenti per l'unione n: m:
- productCategoryPointer: ProdCatID, ProductID, CategoryID.
Dovrai aggiungere alcuni join alle tue query, ma con gli indici giusti, sarai in grado di interrogare rapidamente i tuoi dati.
Si potrebbe provare qualcosa di più orientato.
1.Definire una tabella di base per i Prodotti
Products(ProductID, CategoryID, <any other common properties>)
2.Definire una tabella Categorie
Categories(CategoryID, Name, Description, ..)
Da qui si hanno un sacco di opzioni e quasi tutti di loro di rompere la normalizzazione del database.
Soluzione A.
Sarà un aggiornamento incubo, se è necessario aggiungere nuovi prodotti
A1.Definire una tabella separata per ciascuna delle categorie
Cars(CarID, ProductID, ..)
Pets(PetID, ProductID, ..)
A2.Join le tabelle in base alle relazioni in modo da poter utilizzare i dati
SELECT <fields> FROM Cars INNER JOIN Products ON Cars.ProductID = Products.ProductID
Soluzione B.
Manutenzione incubo per diverse tipologie di immobili (es.int varchar, ecc)
B1.Definire una tabella di Proprietà
CategoryProperty (CPID, Name, Type)
B2.Definire una tabella per contenere le associazioni tra le Categorie e le Proprietà
PropertyAssociation (CPID, PropertyID)
B12.Definire una tabella per mantenere le proprietà (in Alternativa per B1 e B2)
Properties(CategoryID, PropertyID, Name, Type)
B3.Per ogni tipo di struttura (int, double, varchar, etc.) aggiungere una tabella dei valori
PropertyValueInt(ProductID, CPID, PropertyID, Value)
- per int
PropertyValueString(ProductID, CPID, PropertyID, Value)
- per le stringhe
PropertyValueMoney(ProductID, CPID, PropertyID, Value)
- per i soldi
B4.Unire tutte le tabelle per recuperare la proprietà desiderata.
Utilizzando questo approccio, non dovrà gestire tutte le proprietà in una tabella separata, ma il valore tipi di loro.Fondamentalmente tutte le tabelle coinvolte saranno le tabelle di ricerca.Lo svantaggio è che, al fine di recuperare ogni valore, è necessario "Caso" per ogni tipo di valore.
Prendere in considerazione questi articoliqui e qui) quando la scelta di uno di questi approcci. Questo post del forum è anche interessante e in qualche modo legati al tema, anche se è la localizzazione.
Si potrebbe anche usare Tomalak risposta e aggiungere la tipizzazione forte se si sente il bisogno.
Di recente ho dovuto farlo e sto usando NHibernate dove ho tre entità
Categoria opzione prodotto categoria opzione
Un prodotto ha 1 * Categorie
Un prodotto ha 1 * Opzione
Un'opzione ha 1 OptionCategory
una volta impostato, è possibile utilizzare la cache di divieto
Saluti