EAV - è davvero male in tutti gli scenari?


65

Sto pensando di utilizzare un modello di entità-attributo-valore (EAV) per alcune delle cose in uno dei progetti, ma tutte le domande al riguardo in Stack Overflow finiscono per rispondere chiamando EAV un anti-pattern.

Ma mi chiedo se sia così sbagliato in tutti i casi.

Diciamo che l'entità del prodotto del negozio, ha caratteristiche comuni, come nome, descrizione, immagine e prezzo, che prendono parte alla logica in molti luoghi e ha caratteristiche (semi) uniche, come l'orologio e il pallone da spiaggia sarebbero descritti da aspetti completamente diversi. Quindi penso che EAV si adatterebbe per archiviare quelle (semi) caratteristiche uniche.

Tutto questo presuppone che, per mostrare l'elenco dei prodotti, siano sufficienti informazioni nella tabella dei prodotti (ciò significa che non è coinvolto l'EAV) e solo quando si mostra un prodotto / si confrontano fino a 5 prodotti / ecc. vengono utilizzati i dati salvati con EAV.

Ho visto un tale approccio nel commercio Magento ed è abbastanza popolare, quindi ci sono casi in cui EAV è ragionevole?


2
@busy_wait Tabelle "Entity-Attibute-Value" - vedi il modello Entity-attributo-valore su Wikipedia .
Ross Patterson,

Per un esempio del modello EAV che funziona davvero bene, dai un'occhiata al database Datomic. Memorizza tutto nel modello EAVT (T è un "timestamp", in realtà più simile a un ID di transazione). La loro [documentazione di indicizzazione] (docs.datomic.com/indexes.html) sembra mostrarlo al meglio. Per un esempio di EAV che funziona terribilmente, vedi Wordpress .
Dan Ross,

Risposte:


81

https://web.archive.org/web/20140831134758/http://www.dbforums.com/database-concepts-design/1619660-otlt-eav-design-why-do-people-hate.html

EAV offre allo sviluppatore una flessibilità per definire lo schema in base alle esigenze e ciò è utile in alcune circostanze.

D'altra parte si comporta molto male nel caso di una query mal definita e può supportare altre cattive pratiche.

In altre parole, EAV ti dà abbastanza corda per impiccarti e in questo settore, le cose dovrebbero essere progettate al livello più basso di complessità perché il ragazzo che ti sostituirà nel progetto sarà probabilmente un idiota.


32
Adoro l'ultima frase.
Zohar Peled,

2
Link marcio. Esiste una versione cache da qualche parte?
Wildcard il

1
Non seguire il link. La pagina si carica lentamente e non è utile. Inoltre, i forum vecchio stile come quello puzzano. Usa invece lo overflow dello stack! Valuta le risposte buone / utili e spingi verso il basso nel cestino.
Jess

29

In breve, EAV è utile quando il tuo elenco di attributi è spesso in crescita o quando è così grande che la maggior parte delle righe verrebbe riempita con NULL soprattutto se rendessi ogni attributo una colonna. Diventa un anti-pattern se usato al di fuori di quel contesto.


16
Vorrei sostituire "frequentemente" con "ha bisogno della possibilità di essere modificato in fase di esecuzione".
Doc Brown,

3
Possiamo accorciare ulteriormente Doc Brown utilizzando la parola "dinamico" abbastanza ben compresa: EAV è utile quando l'elenco di attributi può cambiare in modo dinamico.
Alexander Mills,

Ancor più a "quando i tuoi attributi possono cambiare" - "dinamicamente" è un po 'ridondante in questo contesto :)
Wranorn

1
È necessariamente più utile di, diciamo, avere il modulo per modificare un attributo eseguire un CREATE TABLEper il nuovo attributo?
Damian Yerrick,

@DamianYerrick approccio interessante. Hai usato questo in produzione?
scavare

21

Diciamo che l'entità del prodotto del negozio, ha caratteristiche comuni, come nome, descrizione, immagine, prezzo, ecc., Che prendono parte alla logica in molti luoghi e ha caratteristiche (semi) uniche, come l'orologio e il pallone da spiaggia sarebbero descritti da aspetti completamente diversi . Quindi penso che EAV si adatterebbe per archiviare quelle (semi) caratteristiche uniche?

L'uso di una struttura EAV ha diverse implicazioni che sono compromessi.

Stai negoziando uno "spazio minore per la riga perché non hai 100 colonne che sono null" contro "query e modello più complessi".

Avere un EAV in genere significa che il valore è una stringa in cui è possibile inserire qualsiasi dato. Ciò ha quindi implicazioni sulla validità e sul controllo dei vincoli. Considera la situazione in cui hai inserito il numero di batterie utilizzate come qualcosa nella tabella EAV. Vuoi trovare una torcia che utilizza batterie di dimensioni C, ma meno di 4.

select P.sku
from
  products P
  attrib Ab on (P.sku = Ab.sku and Ab.key = "batteries")
  attrib Ac on (P.sku = Ac.sku and Ac.key = "count")
where
  cast(Ac.value as int) < 4
  and Ab.value = 'C'
  ...

La cosa da capire qui è che non è possibile utilizzare un indice ragionevolmente sul valore. Inoltre, non puoi impedire a qualcuno di inserire qualcosa che non è un numero intero lì, o un numero intero non valido (usa batterie "-1") perché la colonna del valore viene utilizzata più volte per scopi diversi.

Ciò ha quindi implicazioni nel tentativo di scrivere un modello per il prodotto. Avrai dei simpatici valori digitati ... ma avrai anche Map<String,String>solo una seduta lì con tutti i tipi di cose . Ciò ha quindi ulteriori implicazioni nel serializzarlo su XML o Json e la complessità del tentativo di eseguire convalide o query su tali strutture.

Alcune alternative o modifiche al modello da considerare sono invece di una chiave di formato libero, per avere un'altra tabella con chiavi valide. Significa invece di fare confronti di stringhe nel database, si sta verificando l'uguaglianza degli ID di chiave esterna. La modifica della chiave stessa viene eseguita in un punto. Hai un set di chiavi noto, il che significa che possono essere fatte come enum.

Potresti anche avere tabelle correlate che contengono attributi di una specifica classe di prodotto. Un reparto alimentari potrebbe avere un'altra tabella a cui sono associati diversi attributi che i materiali da costruzione non necessitano (e viceversa).

+----------+    +--------+    +---------+
|Grocery   |    |Product |    |BuildMat |
|id (fk)   +--->|id (pk) |<---+id (fk)  |
|expiration|    |desc    |    |material |
|...       |    |img     |    |...      |
+----------+    |price   |    +---------+
                |...     |               
                +--------+               

Ci sono momenti che richiedono in particolare una tabella EAV.

Considera la situazione in cui non stai semplicemente scrivendo un sistema di inventario per la tua azienda in cui conosci ogni prodotto e ogni attributo. Ora stai scrivendo un sistema di inventario da vendere ad altre società. Non puoi conoscere tutti gli attributi di ogni prodotto: dovranno definirli.

Un'idea che viene fuori è "lasciamo che il cliente modifichi la tabella" e questo è solo un male (entri nella meta-programmazione per le strutture delle tabelle perché non sai più dove si trovano, possono rovinare regalmente la struttura o corrompere l'applicazione, hanno accesso per fare cose sbagliate e le implicazioni di tale accesso diventano significative). C'è di più su questo percorso su MVC4: come creare un modello in fase di esecuzione?

Invece, si crea l'interfaccia amministrativa su una tabella EAV e si consente che venga utilizzata. Se il cliente desidera creare una voce per "polkadot", questa viene inserita nella tabella EAV e sai già come gestirla.

Un esempio di questo può essere visto nel modello di database per Redmine : puoi vedere la tabella custom_fields e la tabella custom_values ​​- quelle sono parti dell'EAV che permettono di estendere il sistema.


Nota che se trovi che l'intera struttura della tabella assomiglia a EAV anziché relazionale, potresti voler guardare il sapore KV di NoSQL (cassandra, redis, Mongo, ...). Renditi conto che questi spesso presentano altri compromessi nel loro design che potrebbero essere o non essere appropriati per quello per cui lo stai usando. Tuttavia, sono progettati specificamente con l'intento di una struttura EAV.

Potresti voler leggere SQL vs NoSQL per un sistema di gestione dell'inventario

Seguendo questo approccio con un database NoSQL orientato ai documenti (couch, mongo), potresti considerare ogni articolo di inventario come un documento su un disco ... recuperare tutto in un singolo documento è veloce. Inoltre, il documento è strutturato in modo da poter estrarre rapidamente una sola cosa. D'altra parte, la ricerca di tutti i documenti per elementi che corrispondono a un particolare attributo può avere prestazioni inferiori (confronta usando 'grep' con tutti i file) ... è tutto un compromesso.

Un altro approccio sarebbe LDAP in cui si avrebbe una base con tutti i suoi elementi associati, ma si applicherebbero anche ulteriori classi di oggetti ad esso per gli altri tipi di elementi. (consultare Inventario del sistema tramite LDAP )

Una volta che si va su questa strada, si può trovare qualcosa che corrisponde esattamente quello che stai cercando ... se tutto viene fornito con alcuni compromessi.


10

6 anni dopo

Ora che JSON in Postgres è qui, abbiamo un'altra opzione, per coloro che usano Postgres. Se desideri solo allegare alcuni dati extra a un prodotto, le tue esigenze sono abbastanza semplici. Esempio:

CREATE TABLE products (sku VARCHAR(30), shipping_weight REAL, detail JSON);
INSERT INTO products ('beachball', 1.0, '{"colors": ["red", "white"], "diameter": "50cm"}');

SELECT * FROM products;
    sku    | weight |               detail               
-----------+--------+------------------------------------
 beachball |      1 | {"colors": ["red", "white"], "diameter": "50cm"}

Ecco un'introduzione più fluida a JSON in Postgres: https://www.compose.com/articles/is-postgresql-your-next-json-database/ .

Nota che Postgres in realtà memorizza JSONB, non JSON in testo semplice, e supporta gli indici sui campi all'interno di un documento / campo JSONB, nel caso in cui scopri di voler effettivamente eseguire una query su tali dati.

Inoltre, si noti che i campi all'interno di un campo JSONB non possono essere modificati singolarmente con una query UPDATE; dovresti sostituire l'intero contenuto del campo JSONB.

Questa risposta potrebbe non rispondere direttamente alla domanda, ma offre un'alternativa a un modello EAV, che dovrebbe essere considerato da chiunque stia meditando sulla domanda originale.


3
Penso che sia un'ottima idea pubblicare una soluzione alternativa. Solo per tenere traccia degli altri, MS SQL supportava le colonne XML con la possibilità di indicizzarle per un po 'e a partire dal 2016 può fare lo stesso con JSON (anche se JSON non è un tipo di colonna nativo in MS SQL, puoi comunque indicizzarlo ). D'altra parte, da quello che ho letto, il supporto JSON di Postgres è migliore, ad esempio sembra che supporti gli indici sui dati nelle proprietà dell'array JSON.
Giedrius,

1
"... i campi all'interno di un campo JSONB non possono essere modificati individualmente con una query UPDATE; dovresti sostituire l'intero contenuto del campo JSONB." Questo è obsoleto, vero? C'è una jsonb_set()funzione in Postgres 9.5 e versioni successive che è esattamente per questo. (L'articolo che hai collegato ai link a sua volta a un articolo più recente che parla delle aggiunte di funzionalità 9.5 .)
Wildcard

7

In genere le persone guardano dall'altra parte se lo si utilizza per le tabelle di ricerca o in altre situazioni in cui il vantaggio è di non dover creare tabelle per uno o due valori memorizzati. La situazione che stai descrivendo, in cui stai praticamente memorizzando le proprietà degli oggetti, sembra perfettamente normale (e normalizzata). L'allargamento di una tabella per memorizzare un numero variabile di attributi degli articoli è una cattiva idea.

Nel caso generale della memorizzazione di dati disparati in una tabella lunga e sottile ... Non dovresti aver paura di creare nuove tabelle se necessario, e avere solo una o due tabelle lunghe e sottili non è molto meglio che avere solo una o due tavoli grassi corti.

Detto questo, sono noto per l'utilizzo delle tabelle EAV per la registrazione. Hanno una buona utilità.


Si prega di definire "tavolo magro" e "tavolo grasso".
Tulains Córdova, il

@ TulainsCórdova: una tabella "scarna" sarebbe una con poche righe e molte colonne, mentre una tabella ad alta definizione è una con molte colonne e poche righe. Un esempio potrebbe essere la creazione di una tabella di ricerca in cui hai proprietà per dire libri. Una tabella fat avrebbe un record per libro, con molte colonne per specifici pezzi di dati, mentre una tabella thin avrebbe forse quattro colonne id, book, field_name, field_data. Il vantaggio del primo è che ci sono meno record, ma il lato negativo è che alcuni campi possono essere vuoti e l'intera cosa è più difficile da estendere.
Satanicpuppy,

@Satanicpuppy Penso che le tue definizioni magre / grasse siano confuse - sono le stesse. Vuoi dire che una tabella scarna ha poche colonne e molte righe?
Charles Wood,

1

EAV cambia il problema della struttura esplicita, in percezione implicita. Piuttosto che dire X è una tabella con le colonne A e B. Implichi che le colonne A e B formino la tabella X. È il contrario in un certo senso ma non c'è necessariamente una mappatura uno a uno. Si potrebbe dire che A e B sono entrambi associati alla tabella (o al tipo) X e Y. Questo potrebbe essere importante nel dominio più coinvolto in cui il contesto è importante.

Ho studiato Datomic, per questo tipo di approccio e penso che sia un sistema molto utile e potente con limiti su cosa dovresti fare (non che non potresti).

Che EAV sia lento o che "ti dia abbastanza corda per impiccarti" non è un'affermazione con cui concordo. Piuttosto, darei maggiore enfasi ai punti di forza di EAV e se si adatta al tuo spazio problematico, dovresti considerarlo.

La mia esperienza è che si tratta di un approccio alla modellazione quasi illimitato e meraviglioso . Nello specifico, nel caso di Datomic, impongono un insieme semantico sopra ogni cosa. Qualsiasi decisione di modellistica che modella una relazione può andare liberamente da una a molte senza dover riprogettare colonne / tabelle. Puoi anche tornare indietro purché il vincolo non violi l'invariante. È lo stesso sotto il cofano.

Il problema con EAV è stato nella mia mente la mancanza di un'implementazione come Datomic. Dal momento che questa è una domanda su EAV, non voglio andare in giro per Datomic, ma è una di quelle cose in cui penso che abbiano fatto tutto bene rispetto a EAV.

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.