Esiste un nome per questo schema di database di valori chiave?


68

Elaboriamo un feed di dati di routine da un client che ha appena riformattato il proprio database da un modulo che mi sembra familiare (una riga per entità, una colonna per attributo) in uno che non mi è familiare (una riga per entità per attributo):

Prima: una colonna per attributo

ID   Ht_cm   wt_kg   Age_yr  ... 
1      190      82     43    ...
2      170      60     22    ...
3      205      90     51    ...

Dopo: una colonna per tutti gli attributi

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     ...

C'è un nome per questa struttura di database? Quali sono i vantaggi relativi? Il vecchio metodo sembra più semplice posizionare vincoli di validità su attributi specifici (non nulli, non negativi, ecc.) E più facile calcolare le medie. Ma posso vedere come potrebbe essere più semplice aggiungere nuovi attributi senza refactoring del database. È un modo standard / preferito di strutturare i dati?

Risposte:


91

Si chiama Entity-Attribute-Value (a volte anche "coppie nome-valore") ed è un classico caso di "un piolo tondo in un buco quadrato" quando le persone usano il modello EAV in un database relazionale.

Ecco un elenco dei motivi per cui non dovresti usare EAV:

  • Non puoi usare tipi di dati. Non importa se il valore è una data, un numero o denaro (decimale). Sarà sempre costretto a varchar. Questo può essere qualsiasi cosa, da un piccolo problema di prestazioni a un enorme mal di pancia (hai mai dovuto inseguire una variazione di un centesimo in un rapporto di roll-up mensile?).
  • Non è possibile (facilmente) imporre vincoli. Richiede una quantità ridicola di codice per imporre "Ognuno deve avere un'altezza tra 0 e 3 metri" o "L'età non deve essere nulla e> = 0", al contrario delle 1-2 righe che ciascuno di questi vincoli sarebbe in un sistema correttamente modellato.
  • Relativamente a quanto sopra, non puoi facilmente garantire di ottenere le informazioni di cui hai bisogno per ciascun cliente (l'età potrebbe mancare da uno, quindi il prossimo potrebbe mancare la loro altezza ecc.). È possibile farlo, ma è un inferno di molto più difficile di quanto SELECT height, weight, age FROM Client where height is null or weight is null.
  • Anche in questo caso, i dati duplicati sono molto più difficili da rilevare (cosa succede se ti danno due anni per un client? De-EAVing i dati, come sotto, ti darà due file di risultati se hai un attributo raddoppiato. Se un client ha due voci separate per due attributi, otterrai quattro righe dalla query seguente).
  • Non puoi nemmeno garantire che i nomi degli attributi siano coerenti. "Age_yr" potrebbe diventare "AGE_IN_YEARS" o "age". (Certo, questo è meno un problema quando si riceve un estratto rispetto a quando le persone inseriscono dati, ma comunque.)
  • Qualsiasi tipo di query non banale è un disastro completo. Per razionalizzare un sistema EAV a tre attributi in modo da poterlo interrogare in modo razionale sono necessari tre join della tabella EAV.

Confrontare:

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"

Per:

SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c

Ecco un elenco (molto breve) di quando dovresti usare EAV:

  • Quando non c'è assolutamente alcun modo per aggirarlo e devi supportare i dati senza schema nel tuo database.
  • Quando hai solo bisogno di conservare "cose" e non aspettarti di averne bisogno in una forma più strutturata. Attenzione, però, il mostro ha chiamato "requisiti mutevoli".

So di aver appena trascorso questo intero post in dettaglio perché l'EAV è un'idea terribile nella maggior parte dei casi, ma ci sono alcuni casi in cui è necessario / inevitabile. tuttavia, la maggior parte delle volte (incluso l'esempio sopra), sarà molto più seccante di quanto valga la pena. Se hai un requisito per un ampio supporto dell'input di dati di tipo EAV, dovresti cercare di memorizzarli in un sistema di valori-chiave, ad esempio Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.


7
+1 con un preavviso minore: puoi utilizzare i tipi di dati se inserisci i valori di tipi diversi in tabelle diverse (beh, non EAV classico, ma una sorta di miglioramento). (Ma poi arriva un'altra domanda: come fai a sapere il tipo di un nuovo attributo?)
dezso

4
D'accordo, ma aggiungerei che EAV è anche un buon approccio da usare quando si tiene un elenco di cose che sono semanticamente irrilevanti per il proprio sistema (non solo senza schema). Ad esempio un catalogo di prodotti online in cui le caratteristiche del prodotto devono essere archiviate ed elencate. Hai un elenco di coppie chiave / valore da rigurgitare, ma il sistema in realtà non sa né si preoccupa di cosa trattino quelle chiavi o valori. In quella situazione, i pericoli dell'EAV sono irrilevanti.
Joel Brown,

10
@JoelBrown Non ti interessa ORA, ma se lungo la strada un vicepresidente chiede di sapere quante camicie nel catalogo hanno sia bottoni marroni che colletti abbottonati, sarà una cagna una domanda da scrivere. Lo stesso EAV indica normalmente una mancanza di pianificazione o lungimiranza.
JNK,

2
@JoelBrown Non sono in disaccordo sul fatto che abbia un uso (molto piccolo, molto stretto). Ma se è probabile che le informazioni vengano mai interrogate in modo strutturato, probabilmente non dovrebbero essere in EAV
JNK,

4
@JoelBrown Se le tue esigenze aziendali o i dati che stai memorizzando cambiano, anche il tuo modello di dati dovrebbe cambiare . Il tuo modello di dati non dovrebbe essere scolpito nella pietra. Inoltre, per un database relazionale, il 99% delle volte che le persone usano EAV il loro ragionamento si riduce a "Non voglio passare il tempo a pensare a come archiviare i miei dati" piuttosto che a "Considerare tutti i modelli e i modelli di database che conosco, EAV funziona meglio per questo set di dati ". Per ripetere: ci sono casi in cui EAV è utile (e forse anche la risposta "giusta"), ma sono pochi e lontani tra loro.
Simon Righarts,


16

In PostgreSQL, un ottimo modo per gestire le strutture EAV è il modulo aggiuntivo hstore, disponibile per la versione 8.4 o successive. Cito il manuale:

Questo modulo implementa il hstoretipo di dati per la memorizzazione di set di coppie chiave / valore all'interno di un singolo valore PostgreSQL. Ciò può essere utile in vari scenari, ad esempio righe con molti attributi che vengono esaminati raramente o dati semi-strutturati. Chiavi e valori sono semplicemente stringhe di testo.

Da Postgres 9.2 esiste anche il jsontipo e una miriade di funzionalità (la maggior parte è stata aggiunta con 9.3 ).

Postgres 9.4 aggiunge il tipo di dati "binario JSON" (in gran parte superiore!) jsonbAll'elenco delle opzioni. Con opzioni di indice avanzate.


10

Se si dispone di un database che utilizza la struttura EAV, è possibile eseguire una query sui dati in vari modi.

La risposta di Simon @ mostra già come eseguire una query utilizzando più join.

Dati di esempio utilizzati:

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

Se si utilizza un RDBMS con una PIVOTfunzione ( SQL Server 2005+ / Oracle 11g + ), è possibile eseguire una query sui dati nel modo seguente:

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;

Vedi SQL Fiddle with Demo

Se non si ha accesso a una PIVOTfunzione, è possibile utilizzare una funzione aggregata con CASEun'istruzione per restituire i dati:

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

Vedi SQL Fiddle with Demo

Entrambe queste query restituiranno dati nel risultato:

| ID | HT_CM | WT_KG | AGE_YR |
-------------------------------
|  1 |   190 |    82 |     43 |
|  2 |   170 |    60 |     22 |
|  3 |   205 |    90 |     51 |

10

Divertente vedere come il modello db EAV è criticato e persino considerato come un "anti-pattern" da alcuni.

Per quanto mi riguarda, i principali svantaggi sono:

  • La curva di apprendimento è ripida se si ottiene su un progetto che già iniziato a utilizzare EAV qualche tempo fa. In effetti, le query sono difficili man mano che aumenti notevolmente il numero di join (e tabelle) e quindi ti verrà chiesto più tempo per la comprensione. Dai un'occhiata al progetto Magento e vedi come lo sviluppatore esterno al progetto ha difficoltà a lavorare sul DB, ma la documentazione è ben sostenuta.
  • Non adatto per la segnalazione , se è necessario ottenere il numero di persone il cui nome inizia con "M", ecc.

Tuttavia, non dovresti assolutamente scartare questa soluzione, ed ecco perché:

  • Simon ha parlato del mostro chiamato "requisiti mutevoli". Mi piace questa espressione :). E IMHO è proprio per questo che EAV può essere un buon candidato, perché è adatto per il "cambiamento" , in quanto puoi aggiungere tutti gli attributi che desideri abbastanza facilmente. Naturalmente dipende dai requisiti che stiamo cambiando. Se stiamo parlando di un business completamente nuovo, ovviamente dovrai rivedere il tuo modello di dati, ma EAV offre molta flessibilità. Solo perché richiede più rigore, non significa che questo sia meno interessante.
  • È stato anche detto che "Non è possibile utilizzare i tipi di dati". : Questo è sbagliato . Puoi benissimo avere diverse tabelle di valori , una per ogni tipo di dati. Devi quindi specificare nella tabella degli attributi quale tipo di dataType è il tuo attributo. In effetti, un mix di classico rapporto / EAV con il rapporto di classe offre molte potenzialità interessanti nella progettazione di dataBase.

2
La curva di apprendimento è più ripida per il primo progetto EAV che si incontra. Dopodiché, sembrano tutti uguali.
ypercubeᵀᴹ

1
Commento temporaneo: non capisco perché il reclamo "non adatto per la segnalazione". EAV sembra ottimo per i rapporti. Seleziona ObjectId da eav.values ​​dove propertyId = nome e valore come 'm%'. Le modifiche allo schema virtuale (ad esempio l'aggiunta di proprietà) possono essere incluse in qualsiasi interfaccia di reporting dinamico (come i menu a discesa) senza dover ricompilare.
crokusek,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.