2013-06-15 11 views
5

Ho un database contenente diversi tipi di prodotto. Ogni tipo contiene campi che differiscono notevolmente tra loro. Il primo tipo di prodotto è classificato in tre categorie. Il secondo tipo di prodotto è classificato in tre categorie. Ma il terzo e il quarto non sono classificati in nulla.Progettazione database per più tipi di prodotti con attributi variabili

Ogni prodotto può avere un numero qualsiasi di proprietà diverse.

Sto usando il modello di database che è fondamentalmente (vedi link) http://www.damirsystems.com/dp_images/product_model_01.png

Ho un database enorme, che contiene circa 500000 prodotto nella tabella di prodotto.

Così quando ho intenzione di recuperare un prodotto dal database con tutti i suoi attributi, o andando a cercare il filtro del prodotto per attributi, ha un effetto negativo sulle prestazioni.

Qualcuno può aiutarmi a capire quale sarà la struttura delle tabelle in sql o fare qualche altra indicizzazione o qualsiasi soluzione fattibile per questo problema. Perché diversi siti di e-commerce utilizzano questo tipo di database e funzionano bene con enormi diversi tipi di prodotti.

risposta

22

Il modello che si collega assomiglia al modello parziale entity–attribute–value (EAV). EAV è molto flessibile, ma offre una scarsa integrità dei dati, è ingombrante e solitamente inefficiente. Non è proprio nello spirito del modello relazionale. Avendo lavorato su alcuni grandi siti di e-commerce, posso dirvi che non si tratta di una pratica di progettazione di database standard o buona in questo campo.

Se non si dispone di un numero enorme di tipi di prodotto (fino a decine, ma non centinaia), è possibile gestirlo utilizzando uno dei due approcci più comuni.

Il primo approccio consiste semplicemente nell'avere un'unica tabella per i prodotti, con colonne per tutti gli attributi che potrebbero essere necessari in ogni diverso tipo di prodotto. Usi qualsiasi colonna appropriata per ogni tipo di prodotto e lascia il resto nullo. Diciamo che vendono libri, musica e video:

create table Product (
    id integer primary key, 
    name varchar(255) not null, 
    type char(1) not null check (type in ('B', 'M', 'V')), 
    number_of_pages integer, -- book only 
    duration_in_seconds integer, -- music and video only 
    classification varchar(2) check (classification in ('U', 'PG', '12', '15', '18')) -- video only 
); 

Questo ha il vantaggio di essere semplice, e di non richiedere unisce. Tuttavia, non fa un buon lavoro di rafforzamento dell'integrità dei dati (ad esempio potresti avere un libro senza un numero di pagine), e se hai più di un paio di tipi di prodotti, la tabella diventerà molto ingombrante .

È possibile gesso sopra il problema di integrità con vincoli di controllo a livello di tabella che richiedono ogni tipo di prodotti di avere valori alcune colonne, come questo:

check ((case when type = 'B' then (number_of_pages is not null) else true end))) 

(Punta di cappello a Joe Celko lì - ho guardato come fare implicazione logica in SQL, e ha trovato un esempio in cui lo fa con questa costruzione per costruire un vincolo di controllo molto simile)

si potrebbe anche dire:!

check ((case when type = 'B' then (number_of_pages is not null) else (number_of_pages is null) end))) 

Per garantire che nessuna riga abbia un valore in una colonna non appropriato al suo tipo.

Il secondo approccio consiste nell'utilizzare più tabelle: una tabella di base contenente colonne comuni a tutti i prodotti e una tabella ausiliaria per ciascun tipo di prodotto contenente colonne specifiche per prodotti di quel tipo.Quindi:

create table Product (
    id integer primary key, 
    type char(1) not null check (type in ('B', 'M', 'V')), 
    name varchar(255) not null 
); 

create table Book (
    id integer primary key references Product, 
    number_of_pages integer not null 
); 

create table Music (
    id integer primary key references Product, 
    duration_in_seconds integer not null 
); 

create table Video (
    id integer primary key references Product, 
    duration_in_seconds integer not null, 
    classification varchar(2) not null check (classification in ('U', 'PG', '12', '15', '18')) 
); 

Nota che le tabelle ausiliari hanno la stessa chiave primaria della tabella principale; la loro colonna chiave principale è anche una chiave esterna per la tabella principale.

Questo approccio è ancora abbastanza semplice, e fa un lavoro migliore di rafforzamento dell'integrità. Query tipicamente richiede unisce, però:

select 
    p.id, 
    p.name 
from 
    Product p 
    join Book b on p.id = b.id 
where 
    b.number_of_pages > 300; 

integrità è ancora perfetto, perché è possibile creare una riga in una tabelle ausiliarie corrispondenti ad una fila di tipo errato nella tabella principale, o per creare righe più tabelle ausiliarie corrispondenti a una singola riga nella tabella principale. Puoi sistemarlo perfezionando ulteriormente il modello. Se si rende la chiave primaria una chiave composta che include la colonna del tipo, il tipo di un prodotto è incorporato nella sua chiave primaria (un libro avrebbe una chiave primaria come ('B', 1001)). Dovresti introdurre la colonna type nelle tabelle ausiliarie in modo che possano avere chiavi esterne che puntano alla tabella principale, e quel punto puoi aggiungere un vincolo check in ogni tabella ausiliaria che richiede che il tipo sia corretto. Come questo:

create table Product (
    type char(1) not null check (type in ('B', 'M', 'V')), 
    id integer not null, 
    name varchar(255) not null, 
    primary key (type, id) 
); 

create table Book (
    type char(1) not null check (type = 'B'), 
    id integer not null, 
    number_of_pages integer not null, 
    primary key (type, id), 
    foreign key (type, id) references Product 
); 

Questo rende anche più facile per interrogare le tabelle giusti dato solo una chiave primaria - si può immediatamente dire che tipo di prodotto si riferisce a senza dover interrogare la tabella principale prima.

Tuttavia, è ancora possibile creare righe nella tabella principale senza righe corrispondenti in alcuna tabella ausiliaria. Non so a mano a mano come sistemarlo.

C'è anche un potenziale problema di duplicazione delle colonne, come nello schema sopra, dove la colonna della durata è duplicata in due tabelle. Si può rimediare con l'introduzione di tabelle ausiliari intermedi per le colonne condivise:

create table Media (
    type char(1) not null check (type in ('M', 'V')), 
    id integer not null, 
    duration_in_seconds integer not null, 
    primary key (type, id), 
    foreign key (type, id) references Product 
); 

create table Music (
    type char(1) not null check (type = 'M'), 
    id integer not null, 
    primary key (type, id), 
    foreign key (type, id) references Product 
); 

create table Video (
    type char(1) not null check (type = 'V'), 
    id integer not null, 
    classification varchar(2) not null check (classification in ('U', 'PG', '12', '15', '18')), 
    primary key (type, id), 
    foreign key (type, id) references Product 
); 

Non si potrebbe pensare che valeva lo sforzo supplementare. Tuttavia, quello che si potrebbe prendere in considerazione facendo è la miscelazione dei due approcci (tavolo singolo e tavolo ausiliario) per affrontare situazioni come questa, e con un tavolo condiviso per alcuni tipi simili di prodotti:

create table Media (
    type char(1) not null check (type in ('M', 'V')), 
    id integer not null, 
    duration_in_seconds integer not null, 
    classification varchar(2) check (classification in ('U', 'PG', '12', '15', '18')), 
    primary key (type, id), 
    foreign key (type, id) references Product, 
    check ((case when type = 'V' then (classification is not null) else (classification is null) end))) 
); 

che sarebbe particolarmente utile se ci fossero tipi simili di prodotti che sono stati raggruppati insieme nell'applicazione. In questo esempio, se la tua vetrina presenta audio e video insieme, ma separatamente per i libri, questa struttura potrebbe supportare un recupero molto più efficiente rispetto a disporre di tabelle ausiliarie separate per ciascun tipo di supporto.

+0

Grazie, per il vostro aiuto. Ma penso che questo non mi aiuterà molto. – user2455135

+1

Oh bene, è un peccato. Buona fortuna a trovare una soluzione! –

+0

@TomAnderson questa è una risposta affascinante e completa. Potresti fornire ulteriori informazioni sul motivo per cui non sei un fan del design del valore di attributo di entità? Grazie – mils

Problemi correlati