Proponowany schemat
Przede wszystkim oto przykład mojego proponowanego schematu, do którego można odwoływać się w całym poście:
Clothes
----------
ClothesID (PK) INT NOT NULL
Name VARCHAR(50) NOT NULL
Color VARCHAR(50) NOT NULL
Price DECIMAL(5,2) NOT NULL
BrandID INT NOT NULL
...
Brand_1
--------
ClothesID (FK/PK) int NOT NULL
ViewingUrl VARCHAR(50) NOT NULL
SomeOtherBrand1SpecificAttr VARCHAR(50) NOT NULL
Brand_2
--------
ClothesID (FK/PK) int NOT NULL
PhotoUrl VARCHAR(50) NOT NULL
SomeOtherBrand2SpecificAttr VARCHAR(50) NOT NULL
Brand_X
--------
ClothesID (FK/PK) int NOT NULL
SomeOtherBrandXSpecificAttr VARCHAR(50) NOT NULL
Opis problemu
Mam stolik z ubraniami, który ma kolumny takie jak nazwa, kolor, cena, brandid i tak dalej, aby opisać atrybuty konkretnego elementu odzieży.
Oto mój problem: różne marki odzieży wymagają różnych informacji. Jaka jest najlepsza praktyka radzenia sobie z takim problemem?
Pamiętaj, że dla moich celów konieczne jest znalezienie informacji specyficznych dla marki, poczynając od pozycji ubrania . Wynika to z tego, że najpierw wyświetlam użytkownikowi informacje z pozycji dotyczącej ubrań , a następnie muszę użyć informacji specyficznych dla marki, aby kupić produkt. Podsumowując, musi istnieć kierunkowa zależność między ubraniami (od) a tabelami brand_x .
Proponowane / aktualne rozwiązanie
Aby sobie z tym poradzić, pomyślałem o następującym schemacie projektu:
Ubrania tabela mają zupełnie kolumnę, która może mieć wartości identyfikacyjnych od 1 do X, gdzie poszczególne odpowiada identyfikacyjnych tabeli danej marki. Na przykład wartość identyfikatora 1 będzie odpowiadać tabeli brand_1 (która może mieć kolumnę URL ), identyfikator 2 będzie odpowiadać brand_2 (która może mieć kolumnę dostawcy ) itp.
Tak więc, aby powiązać konkretny wpis dotyczący ubrania z informacjami dotyczącymi marki, wyobrażam sobie, że logika na poziomie aplikacji będzie wyglądać mniej więcej tak:
clothesId = <some value>
brand = query("SELECT brand FROM clothes WHERE id = clothesId")
if (brand == 1) {
// get brand_1 attributes for given clothesId
} else if (brand == 2) {
// get brand_2 attributes for given clothesId
} ... etc.
Inne komentarze i przemyślenia
Próbuję znormalizować całą bazę danych w BCNF i chociaż to właśnie wymyśliłem, wynikowy kod aplikacji sprawia, że czuję się bardzo niespokojny. Nie ma sposobu na wymuszenie relacji poza poziomem aplikacji, a zatem projekt jest bardzo zepsuty i, jak sądzę, bardzo podatny na błędy.
Badania
Przed napisaniem posta sprawdziłem poprzednie wpisy. Oto post z prawie identycznym problemem, który udało mi się znaleźć. I tak napisałem ten post, ponieważ wydaje się, że jedyna podana odpowiedź nie ma rozwiązania SQL ani rozwiązania projektowego (tj. Wspomina OOP, dziedziczenie i interfejsy).
Jestem także nowicjuszem, jeśli chodzi o projektowanie baz danych, dlatego doceniłbym wszelkie spostrzeżenia.
Wygląda na to, że na przepełnieniu stosu znajdują się bardziej pomocne odpowiedzi:
- Tutaj
- I tu
- Aaaa tutaj (kluczową koncepcją jest: dziedziczenie tabeli klas)
Odniosłem się do tamtejszych rozwiązań i sugeruję, aby inni również znaleźli moje pytanie.
Pomimo podanych powyżej linków wciąż szukam odpowiedzi tutaj i doceniłbym wszelkie dostarczone rozwiązania!
Używam PostgreSQL.
źródło
Opisujesz, przynajmniej częściowo, katalog produktów. Masz kilka atrybutów, które są wspólne dla wszystkich produktów. Należą one do dobrze znormalizowanej tabeli.
Poza tym masz szereg atrybutów, które są specyficzne dla marki (i spodziewam się, że mogą być specyficzne dla produktu). Co twój system musi zrobić z tymi konkretnymi atrybutami? Czy masz logikę biznesową, która zależy od schematu tych atrybutów, czy po prostu umieszczasz je w szeregu par „etykieta”: „wartość”?
Inne odpowiedzi sugerują użycie tak naprawdę podejścia CSV (czy to jest,
JSON
czyARRAY
nie) - Podejścia te rezygnują z regularnej obsługi schematu relacyjnego poprzez przeniesienie schematu z metadanych do samych danych.Istnieje przenośny wzorzec projektowy, który bardzo dobrze pasuje do relacyjnych baz danych. Jest to EAV (encja-atrybut-wartość). Jestem pewien, że czytałeś w wielu, wielu miejscach, że „EAV is Evil” (i tak jest). Istnieje jednak jedna konkretna aplikacja, w której problemy z EAV nie są ważne, a mianowicie katalogi atrybutów produktu.
Wszystkie zwykłe argumenty przeciwko EAV nie dotyczą katalogu funkcji produktu, ponieważ wartości funkcji produktu są zazwyczaj zwracane tylko do listy, a najgorsze do tabeli porównawczej.
Użycie
JSON
typu kolumny powoduje wymuszenie wszelkich ograniczeń danych z bazy danych i zmusza je do logiki aplikacji. Ponadto użycie jednej tabeli atrybutów dla każdej marki ma następujące wady:Odzyskiwanie danych o produkcie z funkcjami specyficznymi dla marki nie jest szczególnie trudne. Prawdopodobnie łatwiej jest stworzyć dynamiczny SQL przy użyciu modelu EAV, niż przy użyciu modelu tabeli dla kategorii. W tabeli dla kategorii potrzebujesz refleksji (lub swojej
JSON
), aby dowiedzieć się, jakie są nazwy kolumn funkcji. Następnie możesz zbudować listę elementów dla klauzuli where. W modelu EAVWHERE X AND Y AND Z
staje sięINNER JOIN X INNER JOIN Y INNER JOIN Z
, więc zapytanie jest nieco bardziej skomplikowane, ale logika budowania zapytania jest nadal całkowicie zależna od tabeli i będzie bardziej niż wystarczająco skalowalna, jeśli masz zbudowane odpowiednie indeksy.Istnieje wiele powodów, dla których nie należy stosować EAV jako ogólnego podejścia. Te powody nie dotyczą katalogu funkcji produktu, więc nie ma nic złego w EAV w tej konkretnej aplikacji.
Z pewnością jest to krótka odpowiedź na złożony i kontrowersyjny temat. Odpowiedziałem wcześniej na podobne pytania i podałem więcej szczegółów na temat ogólnej niechęci do EAV. Na przykład:
Powiedziałbym, że EAV jest ostatnio używany rzadziej niż kiedyś, głównie z dobrych powodów. Myślę jednak, że nie jest to również dobrze zrozumiane.
źródło
Korzystanie z JSON i PostgreSQL
Myślę, że czynisz to trudniejszym, niż to musi być, i później zostaniesz ugryziony. Nie potrzebujesz modelu Entity – atrybut – wartość, chyba że faktycznie potrzebujesz EAV.
Nie ma absolutnie nic złego w tym schemacie.
Teraz możesz wykonać zapytanie za pomocą prostego łączenia
I każdy z operatorów JSON działa w klauzuli where.
Na marginesie, nie umieszczaj adresów URL w bazie danych. Zmieniają się z czasem. Po prostu stwórz funkcję, która je przyjmuje.
lub cokolwiek. Jeśli korzystasz z PostgreSQL, możesz nawet używać skrótów .
Na szczególną uwagę zasługuje również zapis
jsonb
binarny (a więc -'b) i indeksowanie, SARGable lub cokolwiek innego, jak nazywają to teraz fajne dzieci:CREATE INDEX ON brands USING gin ( attributes );
Różnica polega na prostocie zapytania.
Co powiesz na inny ...
źródło
Jednym łatwym rozwiązaniem jest uwzględnienie wszystkich możliwych atrybutów jako kolumn na głównym stole z ubraniami i nadanie wszystkim kolumnom specyficznym dla marki wartości dopuszczalnej. To rozwiązanie łamie normalizację bazy danych, ale jest bardzo łatwe do wdrożenia.
źródło