Gibt es einen Namen für dieses Datenbankschema mit Schlüsselwerten?


68

Wir verarbeiten einen routinemäßigen Datenfeed eines Kunden, der gerade seine Datenbank von einem mir vertrauten Formular (eine Zeile pro Entität, eine Spalte pro Attribut) auf ein mir unbekanntes Formular (eine Zeile pro Entität pro Attribut) umgestaltet hat:

Vorher: eine Spalte pro Attribut

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

Nachher: ​​eine Spalte für alle Attribute

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

Gibt es einen Namen für diese Datenbankstruktur? Was sind die relativen Vorteile? Die alte Methode scheint es einfacher zu sein, Gültigkeitsbeschränkungen für bestimmte Attribute (nicht null, nicht negativ usw.) festzulegen und Durchschnittswerte leichter zu berechnen. Aber ich kann sehen, wie es einfacher sein könnte, neue Attribute hinzuzufügen, ohne die Datenbank umzugestalten. Ist dies eine Standard- / bevorzugte Art der Datenstrukturierung?

Antworten:


91

Es heißt Entity-Attribute-Value (manchmal auch "Name-Wert-Paare") und ist ein klassischer Fall von "einem runden Stift in einem quadratischen Loch", wenn Menschen das EAV-Muster in einer relationalen Datenbank verwenden.

Hier ist eine Liste, warum Sie EAV nicht verwenden sollten :

  • Sie können keine Datentypen verwenden. Es spielt keine Rolle, ob der Wert ein Datum, eine Zahl oder ein Geld (dezimal) ist. Es wird immer zu Varchar gezwungen werden. Dies kann alles Mögliche sein, von einem kleinen Leistungsproblem bis hin zu einem massiven Bauchweh (mussten Sie jemals eine 1-Cent-Variante in einem monatlichen Roll-up-Bericht runterjagen?).
  • Sie können Einschränkungen nicht (leicht) erzwingen. Es erfordert eine lächerliche Menge an Code, um "Jeder muss eine Höhe zwischen 0 und 3 Metern haben" oder "Alter darf nicht null und> = 0 sein" zu erzwingen, im Gegensatz zu den 1-2 Zeilen, die jede dieser Einschränkungen sein würde in einem richtig modellierten System.
  • In Bezug auf das oben Gesagte können Sie nicht ohne Weiteres garantieren, dass Sie die Informationen erhalten, die Sie für jeden Kunden benötigen (bei einem fehlt möglicherweise das Alter, bei dem nächsten fehlt möglicherweise die Größe usw.). Du kannst es schaffen, aber es ist verdammt viel schwieriger als SELECT height, weight, age FROM Client where height is null or weight is null.
  • Erneut ist es viel schwieriger, doppelte Daten zu erkennen (was passiert, wenn Sie für einen Client zwei Altersstufen angeben? Wenn Sie die Daten wie unten angegeben deaktivieren, erhalten Sie zwei Ergebniszeilen, wenn Sie ein doppeltes Attribut haben. Wenn ein Client verwendet wird hat zwei separate Einträge für zwei Attribute, Sie erhalten vier Zeilen aus der Abfrage unten).
  • Sie können nicht einmal garantieren, dass die Attributnamen konsistent sind. "Age_yr" kann "AGE_IN_YEARS" oder "age" werden. (Zugegeben, dies ist weniger problematisch, wenn Sie einen Auszug erhalten, als wenn Leute Daten einfügen, aber dennoch.)
  • Jede Art von nicht trivialer Abfrage ist eine völlige Katastrophe. Um ein EAV-System mit drei Attributen zu relationalisieren, sodass Sie es auf rationale Weise abfragen können, sind drei Joins der EAV-Tabelle erforderlich.

Vergleichen Sie:

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"

Zu:

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

Hier ist eine (sehr kurze) Liste, wann Sie EAV verwenden sollten:

  • Wenn es gibt absolut keinen Weg drum herum und Sie haben Schema-weniger Daten in Ihrer Datenbank zu unterstützen.
  • Wenn Sie nur "Zeug" speichern müssen und nicht erwarten, dass es in einer strukturierteren Form benötigt wird. Vorsicht, das Monster nennt sich "wechselnde Anforderungen".

Ich weiß, dass ich diesen ganzen Beitrag damit verbracht habe, zu erklären, warum EAV in den meisten Fällen eine schreckliche Idee ist - aber es gibt einige Fälle, in denen es notwendig / unvermeidbar ist. In den meisten Fällen (einschließlich des obigen Beispiels) ist dies jedoch weitaus mühsamer als es sich lohnt. Wenn Sie eine breite Unterstützung für die Dateneingabe vom Typ EAV benötigen, sollten Sie die Speicherung in einem Schlüsselwertsystem in Betracht ziehen, z. B. Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.


7
+1 mit einem kleinen Hinweis: Sie können Datentypen verwenden, wenn Sie die Werte verschiedener Typen in verschiedenen Tabellen ablegen (also nicht klassisches EAV, sondern eine Art Verbesserung). (Aber dann kommt eine zusätzliche Frage:
Woher

4
Einverstanden, aber ich möchte hinzufügen, dass EAV auch ein guter Ansatz ist, wenn Sie eine Liste von Dingen führen, die für Ihr System semantisch irrelevant sind (nicht nur schemafrei). Zum Beispiel ein Online-Produktkatalog, in dem die Produkteigenschaften gespeichert und aufgelistet werden müssen. Sie haben eine Liste von Schlüssel / Wert-Paaren, die nachgesagt werden müssen, aber das System weiß nicht genau, worum es bei diesen Schlüsseln oder Werten geht. In dieser Situation sind die Gefahren von EAV irrelevant.
Joel Brown

10
@JoelBrown Es ist dir JETZT egal, aber wenn dich ein Vizepräsident später fragt, wie viele Hemden im Katalog sowohl braune Knöpfe als auch Button-down-Kragen haben, ist es eine verdammte Frage, die du stellen musst. EAV selbst weist normalerweise auf einen Mangel an Planung oder Voraussicht hin.
JNK

2
@ JoelBrown Ich bin nicht anderer Meinung, dass es eine (sehr kleine, sehr enge) Verwendung hat. Aber wenn die Informationen jemals in strukturierter
JNK

4
@JoelBrown Wenn sich Ihre Geschäftsanforderungen oder die Daten, die Sie speichern, ändern, sollte sich auch Ihr Datenmodell ändern . Ihr Datenmodell sollte nicht in Stein gemeißelt sein. Bei einer relationalen Datenbank läuft die Argumentation von EAV in 99% der Fälle darauf hinaus, "Ich möchte keine Zeit damit verbringen, über das Speichern meiner Daten nachzudenken", anstatt "alle mir bekannten Datenbankmuster und -modelle zu berücksichtigen". EAV funktioniert am besten für diesen Datensatz ". Um es zu wiederholen - es gibt Fälle, in denen EAV nützlich ist (und vielleicht sogar die 'richtige' Antwort), aber es gibt nur wenige Fälle, in denen dies sinnvoll ist.
Simon Righarts

18

Entitätsattributwert (EAV)

Es wird von vielen als Anti-Muster angesehen, auch von mir.

Hier sind Ihre Alternativen:

  1. Verwenden Sie die Vererbung von Datenbanktabellen

  2. Verwenden Sie XML-Daten und SQLXML-Funktionen

  3. Verwenden Sie eine NOSQL-Datenbank wie HBase


3
Auf jeden Fall ein Anti-Pattern für die meisten Anwendungsfälle. Wenn Sie einen wirklich kleinen Datensatz haben und die Leistung keine Rolle spielt, funktioniert dies möglicherweise für Sie.
JNK

16

In PostgreSQL ist ein sehr guter Weg, mit EAV-Strukturen umzugehen, das Zusatzmodul hstore, das für Version 8.4 oder höher verfügbar ist. Ich zitiere das Handbuch:

Dieses Modul implementiert den hstoreDatentyp zum Speichern von Sätzen von Schlüssel / Wert-Paaren in einem einzelnen PostgreSQL-Wert. Dies kann in verschiedenen Szenarien hilfreich sein, z. B. bei Zeilen mit vielen Attributen, die selten untersucht werden, oder bei halbstrukturierten Daten. Schlüssel und Werte sind einfach Textzeichenfolgen.

Seit Postgres 9.2 gibt es auch den jsonTyp und eine Vielzahl von Funktionen, die dazugehören (die meisten wurden mit 9.3 hinzugefügt ).

Postgres 9.4 fügt jsonbder Liste der Optionen den (größtenteils überlegenen!) "Binären JSON" -Datentyp hinzu . Mit erweiterten Indexoptionen.


10

Wenn Sie über eine Datenbank verfügen, die die EAV-Struktur verwendet, können Sie die Daten auf verschiedene Arten abfragen.

Die Antwort von @ Simon zeigt bereits, wie eine Abfrage mit mehreren Joins ausgeführt wird.

Verwendete Beispieldaten:

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

Wenn Sie ein RDBMS mit einer PIVOTFunktion verwenden ( SQL Server 2005+ / Oracle 11g + ), können Sie die Daten folgendermaßen abfragen:

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;

Siehe SQL Fiddle with Demo

Wenn Sie keinen Zugriff auf eine PIVOTFunktion haben, können Sie eine Aggregatfunktion mit einer CASEAnweisung verwenden, um die Daten zurückzugeben:

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

Siehe SQL Fiddle with Demo

Diese beiden Abfragen geben Daten im Ergebnis zurück:

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

10

Es ist lustig zu sehen, wie das EAV-Datenbankmodell von einigen kritisiert und sogar als "Anti-Pattern" angesehen wird.

Für mich sind die Hauptnachteile :

  • Die Lernkurve ist steiler, wenn Sie ein Projekt starten, das EAV bereits vor einiger Zeit verwendet hat. Tatsächlich sind die Abfragen schwierig, da Sie die Anzahl der Joins (und Tabellen) erheblich erhöhen und Sie mehr Zeit zum Verstehen benötigen. Schauen Sie sich einfach das Magento-Projekt an und sehen Sie, wie schwer es den Entwicklern außerhalb des Projekts fällt, an der DB zu arbeiten, aber die Dokumentation ist gut erhalten.
  • Nicht für die Berichterstellung geeignet , wenn Sie die Anzahl der Personen ermitteln möchten, deren Name mit "M" usw. beginnt.

Sie sollten diese Lösung jedoch auf keinen Fall verwerfen. Deshalb:

  • Simon sprach über das Monster mit dem Namen "wechselnde Anforderungen". Ich mag diesen Ausdruck :). Und genau deshalb ist EAV meiner Meinung nach ein guter Kandidat, da dies gut für "Veränderungen" geeignet ist , da Sie so viele Attribute hinzufügen können, wie Sie möchten. Natürlich hängt es von den Anforderungen ab, die wir ändern. Wenn es sich um ein völlig neues Geschäft handelt, müssen Sie natürlich Ihr dataModel überprüfen, aber EAV bietet viel Flexibilität. Nur weil es nach mehr Strenge verlangt, heißt das nicht, dass dies weniger interessant ist.
  • Es wurde auch gesagt, dass "Sie Datentypen nicht verwenden können." : Das ist falsch . Möglicherweise haben Sie mehrere Wertetabellen , eine für jeden Datentyp. Sie müssen dann in Ihrer Attributtabelle angeben, welche Art von Datentyp Ihr ​​Attribut ist. Tatsächlich bietet eine Mischung aus klassischer Relation / EAV mit Klassenbeziehung eine Menge interessanter Potenziale im Datenbankdesign.

2
Die Lernkurve ist steiler, wenn man auf das erste EAV-Design stößt. Danach sehen alle gleich aus.
ypercubeᵀᴹ

1
Temporärer Kommentar: Ich verstehe nicht, warum die Behauptung "nicht für die Berichterstellung geeignet" ist. EAV eignet sich hervorragend für die Berichterstattung. Wählen Sie ObjectId aus eav.values ​​aus, wobei propertyId = name und value wie 'm%' ist. Änderungen am virtuellen Schema (z. B. Hinzufügen von Eigenschaften) können in beliebige dynamische Berichtsschnittstellen (z. B. Dropdowns) aufgenommen werden, ohne dass eine Neukompilierung erforderlich ist.
Crokusek
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.