Zpracováváme rutinní datový zdroj od klienta, který právě přeformuloval svou databázi z formuláře, který se zdá být známý (jeden řádek na entitu, jeden sloupec na atribut) do podoby, která se mi zdá neznámá (jeden řádek na entitu na atribut):
Před: jeden sloupec na atribut
ID Ht_cm wt_kg Age_yr ...
1 190 82 43 ...
2 170 60 22 ...
3 205 90 51 ...
After: jeden sloupec pro všechny atributy
ID Metric Value
1 Ht_cm 190
1 Wt_kg 82
1 Age_yr 43
1 ...
2 Ht_cm 170
2 Wt_kg 60
2 Age_yr 22
2 ...
3 Ht_cm 205
3 Wt_kg 90
3 Age_yr 51
3 ...
Existuje název této struktury databáze? Jaké jsou relativní výhody? Zdá se, že starý způsob je jednodušší umístit omezení platnosti na specifické atributy (nenulové, nezáporné atd.) A snáze vypočítat průměry. Ale vidím, jak může být snazší přidat nové atributy, aniž by došlo k refaktorizaci databáze. Je to standardní/preferovaný způsob strukturování dat?
Říká se tomu entita-atribut-hodnota (také někdy 'páry název-hodnota') a je to klasický případ „kulatého kolíku ve čtvercové díře“, když lidé používají vzor EAV v relační databázi.
Zde je seznam důvodů, proč byste neměli používat EAV:
SELECT height, weight, age FROM Client where height is null or weight is null
.Porovnat:
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"
Na:
SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c
Zde je (velmi krátký) seznam, kdy byste měli používat EAV:
Vím, že jsem právě strávil celý tento příspěvek podrobně, proč je EAV ve většině případů hrozný nápad - ale existuje několik případů, kdy je to nutné/nevyhnutelné. Většinu času (včetně výše uvedeného příkladu) však bude mnohem obtížnější, než stojí za to. Pokud máte požadavek na širokou podporu vstupu dat typu EAV, měli byste se podívat na jejich uložení v systému klíč-hodnota, např. Hadoop/HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.
Hodnota atributu entity (EAV)
Mnoho, včetně mě, je považováno za anti-vzor.
Zde jsou vaše alternativy:
použijte databázi dědičnost tabulky
použijte data XML a funkce SQLXML
použijte databázi nosql, jako je HBase
V PostgreSQL je velmi dobrým způsobem, jak se vypořádat se strukturami EAV, další modul hstore
, dostupný pro verze 8.4 nebo novější. Cituji manuál:
Tento modul implementuje datový typ
hstore
pro ukládání sad párů klíč/hodnota v rámci jedné hodnoty PostgreSQL. To může být užitečné v různých scénářích, jako jsou řádky s mnoha atributy, které jsou zřídka zkoumány, nebo polostrukturovaná data. Klíče a hodnoty jsou jednoduše textové řetězce.
Od Postgresu 9.2 existuje také typ json
a hostitel s funkcemi (- - většina z nich byla přidána s 9. ).
Postgres 9.4 přidává (převážně lepší!) Datový typ "binární JSON" jsonb
do seznamu možnosti. S pokročilými možnostmi indexu.
Je zvláštní vidět, jak je model EAV db kritizován a někteří dokonce považováni za „anti-model“.
Pokud jde o mě, hlavní nevýhody jsou:
Toto řešení byste však rozhodně neměli zahodit, a zde je důvod:
Pokud máte databázi používající strukturu EAV, je možné data dotazovat různými způsoby.
@ Simonova odpověď již ukazuje, jak provést dotaz pomocí více spojení.
Použitá vzorová data:
CREATE TABLE yourtable ([ID] int, [Metric] varchar(6), [Value] int);
INSERT INTO yourtable ([ID], [Metric], [Value])
VALUES (1, 'Ht_cm', 190),
(1, 'Wt_kg', 82),
(1, 'Age_yr', 43),
(2, 'Ht_cm', 170),
(2, 'Wt_kg', 60),
(2, 'Age_yr', 22),
(3, 'Ht_cm', 205),
(3, 'Wt_kg', 90),
(3, 'Age_yr', 51);
Pokud používáte RDBMS, který má funkci PIVOT
( SQL Server 2005 + / Oracle 11g + ), můžete data dotazovat následujícím způsobem:
select id, Ht_cm, Wt_kg, Age_yr
from
(
select id, metric, value
from yourtable
) src
pivot
(
max(value)
for metric in (Ht_cm, Wt_kg, Age_yr)
) piv;
Pokud nemáte přístup k funkci PIVOT
, můžete pro vrácení dat použít agregační funkci s příkazem CASE
:
select id,
max(case when metric ='Ht_cm' then value else null end) Ht_cm,
max(case when metric ='Wt_kg' then value else null end) Wt_kg,
max(case when metric ='Age_yr' then value else null end) Age_yr
from yourtable
group by id
Oba tyto dotazy vrátí data ve výsledku:
| ID | HT_CM | WT_KG | AGE_YR |
-------------------------------
| 1 | 190 | 82 | 43 |
| 2 | 170 | 60 | 22 |
| 3 | 205 | 90 | 51 |