Nazywa się to Entity-Attribute-Value (czasami także „parami nazwa-wartość”) i jest to klasyczny przypadek „okrągłego kołka w kwadratowym otworze”, gdy ludzie używają wzorca EAV w relacyjnej bazie danych.
Oto lista powodów, dla których nie należy używać EAV:
- Nie możesz używać typów danych. Nie ma znaczenia, czy wartością jest data, liczba, czy pieniądze (dziesiętne). Zawsze będzie zmuszony do varchar. Może to być coś od drobnego problemu z wydajnością do ogromnego bólu jelit (czy kiedykolwiek musiałeś ścigać różnicę jednego centa w miesięcznym raporcie podsumowującym?).
- Nie możesz (łatwo) egzekwować ograniczeń. Wymaga to absurdalnej ilości kodu, aby wymusić „Każdy musi mieć wysokość od 0 do 3 metrów” lub „Wiek nie może być zerowy i> = 0”, w przeciwieństwie do 1-2 linii, że każde z tych ograniczeń byłoby w prawidłowo wymodelowanym systemie.
- W związku z powyższym nie możesz łatwo zagwarantować, że otrzymasz informacje potrzebne każdemu klientowi (w jednym z nich może brakować wieku, a w innym może nie być wzrostu, itp.). Możesz to zrobić, ale jest o wiele trudniejsze niż
SELECT height, weight, age FROM Client where height is null or weight is null
.
- Powiązane ponownie, zduplikowane dane są znacznie trudniejsze do wykrycia (co się stanie, jeśli dadzą ci dwa lata dla jednego klienta? Usunięcie EAV danych, jak poniżej, da ci dwa wiersze wyników, jeśli masz jeden atrybut podwójny. Jeśli jeden klient ma dwa osobne wpisy dla dwóch atrybutów, otrzymasz cztery wiersze z zapytania poniżej).
- Nie możesz nawet zagwarantować, że nazwy atrybutów są spójne. „Age_yr” może być „AGE_IN_YEARS” lub „age”. (Trzeba przyznać, że jest to mniejszy problem, gdy otrzymujesz wyciąg, a kiedy ludzie wstawiają dane, ale nadal.)
- Wszelkie nietrywialne zapytania to kompletna katastrofa. Aby relacjonować trójwymiarowy system EAV w celu racjonalnego zapytania go, wymagane są trzy sprzężenia tabeli EAV.
Porównać:
SELECT cID.ID AS [ID], cH.Value AS [Height], cW.Value AS [Weight], cA.Value AS [Age]
FROM (SELECT DISTINCT ID FROM Client) cID
LEFT OUTER JOIN
Client cW ON cID.ID = cW.ID AND cW.Metric = "Wt_kg"
LEFT OUTER JOIN
Client cH ON cID.ID = cH.ID AND cW.Metric = "Ht_cm"
LEFT OUTER JOIN
Client cA ON cID.ID = cA.ID AND cW.Metric = "Age_yr"
Do:
SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c
Oto (bardzo krótka) lista, kiedy powinieneś używać EAV:
- Gdy nie ma absolutnie żadnej możliwości obejścia tego problemu i musisz obsługiwać dane bez schematu w bazie danych.
- Gdy po prostu potrzebujesz przechowywać „rzeczy” i nie oczekujesz, że będziesz potrzebować ich w bardziej uporządkowanej formie. Uważaj jednak, potwór nazwał „zmieniającymi się wymaganiami”.
Wiem, że właśnie spędziłem cały ten post opisując, dlaczego EAV jest okropnym pomysłem w większości przypadków - ale jest kilka przypadków, w których jest to konieczne / nieuniknione. jednak przez większość czasu (w tym powyższy przykład) będzie to znacznie trudniejsze niż warte. Jeśli masz potrzebę szerokiej obsługi danych typu EAV, powinieneś rozważyć przechowywanie ich w systemie klucz-wartość, np. Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.
Wartość atrybutu jednostki (EAV)
Jest uważany za anty-wzór przez wielu, w tym przeze mnie.
Oto twoje alternatywy:
użyj dziedziczenia tabeli bazy danych
używać danych XML i funkcji SQLXML
użyj bazy danych nosql, takiej jak HBase
źródło
W PostgreSQL jednym bardzo dobrym sposobem radzenia sobie ze strukturami EAV jest dodatkowy moduł
hstore
, dostępny dla wersji 8.4 lub nowszej. Cytuję instrukcję:Od wersji Postgres 9.2 dostępny jest także
json
typ i mnóstwo funkcji ( większość z nich dodana wraz z 9.3 ).Postgres 9.4 dodaje (o wiele lepszy!) Typ danych „binarny JSON”
jsonb
do listy opcji. Z zaawansowanymi opcjami indeksu.źródło
Jeśli masz bazę danych korzystającą ze struktury EAV, możliwe jest zapytanie danych na różne sposoby.
@ Odpowiedź Simona już pokazuje, jak wykonać zapytanie przy użyciu wielu sprzężeń.
Przykładowe użyte dane:
Jeśli używasz RDBMS, który ma
PIVOT
funkcję ( SQL Server 2005+ / Oracle 11g + ), możesz zapytać o dane w następujący sposób:Zobacz SQL Fiddle with Demo
Jeśli nie masz dostępu do
PIVOT
funkcji, możesz użyć funkcji agregującej zCASE
instrukcją, aby zwrócić dane:Zobacz SQL Fiddle with Demo
Oba te zapytania zwrócą dane w wyniku:
źródło
Zabawnie jest widzieć, jak krytykowany jest model db EAV, a nawet przez niektórych uważany za „anty-wzór”.
Według mnie najważniejsze wady to:
Jednak zdecydowanie nie należy odrzucać tego rozwiązania, a oto dlaczego:
źródło