2013-06-15 14 views
5

Mam bazę danych zawierającą różne typy produktów. Każdy typ zawiera pole, które znacznie się między sobą różni. Pierwszy rodzaj produktu jest podzielony na trzy kategorie. Drugi typ produktu jest podzielony na trzy kategorie. Ale trzeci i czwarty nie jest w niczym sklasyfikowany.Projektowanie bazy danych dla wielu typów produktów o zmiennych atrybutach

Każdy produkt może mieć dowolną liczbę różnych właściwości.

Używam modelu bazy danych, która jest w zasadzie (patrz link) http://www.damirsystems.com/dp_images/product_model_01.png

Mam ogromną bazę danych, która zawiera około 500.000 produkt w tabeli produktów.

Kiedy więc zamierzam pobrać produkt z bazy danych ze wszystkimi jego atrybutami lub będę szukał filtrowania produktów według atrybutów, źle wpływa na wydajność.

Czy ktoś może mi pomóc, jaka będzie struktura tabel w sql lub trochę więcej indeksowania lub możliwe rozwiązanie tego problemu Ponieważ różne witryny e-commerce używają tego rodzaju bazy danych i działają dobrze z ogromnymi różnymi rodzajami produktów.

Odpowiedz

22

Model, do którego link prowadzi, wygląda na częściowy model entity–attribute–value (EAV). EAV jest bardzo elastyczny, ale oferuje słabą integralność danych, jest uciążliwy i zwykle nieefektywny. Nie jest to zgodne z duchem modelu relacyjnego. Pracując w dużych witrynach e-commerce, mogę powiedzieć, że nie jest to standardowa lub dobra praktyka projektowania baz danych w tej dziedzinie.

Jeśli nie masz olbrzymiej liczby typów produktów (do dziesiątek, ale nie setek), możesz sobie z tym poradzić za pomocą jednego z dwóch wspólnych podejść.

Pierwszym podejściem jest po prostu posiadanie pojedynczej tabeli dla produktów, z kolumnami dla wszystkich atrybutów, które mogą być potrzebne dla każdego rodzaju produktu. Używasz tych kolumn, które są odpowiednie dla każdego rodzaju produktu, i pozostawiasz resztę pustą. Załóżmy, że sprzedajesz książki, muzykę i wideo:

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 
); 

Ma to tę zaletę, że jest proste i nie wymaga złączeń. Jednak nie działa to dobrze na egzekwowanie integralności danych (na przykład można mieć książkę bez liczby stron), a jeśli masz więcej niż kilka rodzajów produktów, stół stanie się bardzo nieporęczny .

Można tynk nad problemem integralności z ograniczeniami kontrolnych na poziomie tabeli, które wymagają każdy rodzaj produktów mają wartości niektórych kolumn, na przykład:

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

(wskazówka kapelusz Joe Čelko tam - i spojrzał w górę jak zrobić logicznej implikacji w SQL i znalazł przykład, w którym robi to z tej konstrukcji do budowy bardzo podobne ograniczenie wyboru)

można nawet powiedzieć:

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

Aby upewnić się, że żaden wiersz nie ma wartości w kolumnie nieodpowiedniej do jej typu.

Drugim podejściem jest użycie wielu tabel: jednej tabeli bazowej zawierającej kolumny wspólne dla wszystkich produktów i jednej tabeli pomocniczej dla każdego typu kolumn zawierających produkty specyficzne dla produktów tego typu.A więc:

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')) 
); 

Należy zauważyć, że tabele pomocnicze mają ten sam klucz podstawowy, co tabela główna; ich podstawowa kolumna klucza jest również obcym kluczem do głównej tabeli.

To podejście jest nadal dość proste i lepiej sprawdza się w egzekwowaniu uczciwości. Zapytania będą typowo obejmować przyłącza, choć:

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

Uczciwość nie jest jeszcze idealny, ponieważ jest możliwe, aby utworzyć wiersz pomocniczy tabelach odpowiadających rzędu niewłaściwego typu w głównej tabeli, lub do tworzenia wierszy wiele tabel pomocniczych odpowiadających pojedynczemu wierszowi w tabeli głównej. Możesz to naprawić, udoskonalając model dalej. Jeśli klucz podstawowy jest kluczem złożonym, który zawiera kolumnę typu, to typ produktu jest osadzony w jego kluczu podstawowym (książka miałaby klucz podstawowy, taki jak ("B", 1001)). Będziesz musiał wprowadzić kolumnę typu w tabelach pomocniczych, aby mogły one mieć klucze obce wskazujące na główną tabelę, a punkt ten mógł dodać ograniczenie sprawdzające w każdej tabeli pomocniczej, która wymaga, aby typ był poprawny. Tak:

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 
); 

To również sprawia, że ​​łatwiej zapytać odpowiednie tabele podane tylko klucz podstawowy - można od razu powiedzieć, jaki rodzaj produktu, który odnosi się bez uprzedniego zapytania do głównego stołu.

Jednak nadal można tworzyć wiersze w głównej tabeli bez odpowiednich wierszy w dowolnej tabeli pomocniczej. Nie wiem od ręki, jak to naprawić.

Istnieje również potencjalny problem duplikowania kolumn - tak jak w powyższym schemacie, w którym kolumna czasu trwania jest powielana w dwóch tabelach. Można to naprawić poprzez wprowadzenie pośrednich tabele pomocnicze dla współdzielonych kolumnach:

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 
); 

Nie mogą myśleć, że była warta dodatkowego wysiłku. Jednak to, co można rozważyć robi jest mieszanie tych dwóch podejść (Single stołowy i pomocnicze) do czynienia z tego typu sytuacjach, posiadające wspólny stół dla niektórych podobnych rodzajów produktów:

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))) 
); 

który byłby szczególnie przydatny jeśli były podobne produkty, które zostały zgrupowane w aplikacji. W tym przykładzie, jeśli witryna sklepu przedstawia razem audio i wideo, ale oddzielnie dla książek, struktura ta może obsługiwać znacznie bardziej wydajne wyszukiwanie niż oddzielne tabele pomocnicze dla każdego rodzaju nośnika.

+0

Dzięki za pomoc. Ale myślę, że to mi nie pomoże. – user2455135

+1

No cóż, to wstyd. Powodzenia ze znalezieniem rozwiązania! –

+0

@TomAnderson to fascynująca i kompletna odpowiedź. Czy możesz podać więcej informacji, dlaczego nie jesteś fanem projektu encji-wartości atrybutów? Dzięki – mils

Powiązane problemy