Existe-t-il un nom pour ce schéma de base de données de valeurs de clé?

69

Nous traitons un flux de données de routine provenant d'un client qui vient de refactoriser sa base de données à partir d'un formulaire qui semble familier (une ligne par entité, une colonne par attribut) à un formulaire qui me semble inconnu (une ligne par entité par attribut):

Avant: une colonne par attribut

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

Après: une colonne pour tous les attributs

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

Existe-t-il un nom pour cette structure de base de données? Quels sont les avantages relatifs? L'ancienne méthode semble plus facile pour imposer des contraintes de validité sur des attributs spécifiques (non nul, non négatif, etc.) et plus facile pour calculer des moyennes. Mais je peux voir comment il pourrait être plus facile d'ajouter de nouveaux attributs sans refactoriser la base de données. Est-ce un moyen standard / préféré de structurer les données?

prototype
la source

Réponses:

91

Cela s'appelle Entité-Attribut-Valeur (parfois aussi des "paires nom-valeur") et c'est un cas classique de "cheville ronde dans un trou carré" quand les gens utilisent le modèle EAV dans une base de données relationnelle.

Voici une liste des raisons pour lesquelles vous ne devriez pas utiliser EAV:

  • Vous ne pouvez pas utiliser les types de données. Peu importe que la valeur soit une date, un nombre ou de l'argent (décimal). Il sera toujours contraint de varchar. Cela peut aller d'un problème de performance mineur à un mal de ventre énorme (avez-vous déjà dû chasser une variation d'un cent dans un rapport de synthèse mensuel?).
  • Vous ne pouvez pas (facilement) appliquer des contraintes. "Tout le monde doit avoir une hauteur comprise entre 0 et 3 mètres" ou "L'âge ne doit pas être nul et> = 0", par opposition à la ligne 1-2 que chacune de ces contraintes serait dans un système correctement modélisé.
  • En relation avec ce qui précède, vous ne pouvez pas facilement garantir que vous obtenez les informations dont vous avez besoin pour chaque client (l’âge pourrait manquer à l’un, puis le suivant pourrait ne pas être à la hauteur, etc.). Vous pouvez le faire, mais c'est beaucoup plus difficile que ça SELECT height, weight, age FROM Client where height is null or weight is null.
  • De nouveau liés, les données en double sont beaucoup plus difficiles à détecter (que se passe-t-il si elles vous donnent deux âges pour un client? Si vous supprimez les données, comme ci-dessous, vous obtiendrez deux lignes de résultats si vous avez un attribut doublé. Si un client a deux entrées distinctes pour deux attributs, vous obtiendrez quatre lignes de la requête ci-dessous).
  • Vous ne pouvez même pas garantir la cohérence des noms d'attributs. "Age_yr" peut devenir "AGE_IN_YEARS" ou "age". (Certes, le problème est moins grave lorsque vous recevez un extrait que lorsque des personnes insèrent des données, mais quand même.)
  • Toute sorte de requête non triviale est un désastre complet. Pour relationaliser un système EAV à trois attributs afin de pouvoir l'interroger de manière rationnelle, il faut trois jointures de la table EAV.

Comparer:

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"

À:

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

Voici une liste (très courte) des cas où vous devriez utiliser EAV:

  • Lorsqu'il n'y a absolument aucun moyen de le contourner et que vous devez prendre en charge des données sans schéma dans votre base de données.
  • Lorsque vous avez juste besoin de stocker des "choses" et que vous ne vous attendez pas à en avoir besoin sous une forme plus structurée. Attention, le monstre a appelé "les exigences changeantes".

Je sais que je viens de passer tout ce message à expliquer en détail pourquoi EAV est une idée terrible dans la plupart des cas - mais il existe quelques cas où cela est nécessaire / inévitable. Cependant, la plupart du temps (y compris l'exemple ci-dessus), cela va être beaucoup plus compliqué que ça n'en vaut la peine. Si vous avez besoin d'une prise en charge étendue de la saisie de données de type EAV, vous devez envisager de les stocker dans un système de valeurs-clés, par exemple Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.

Simon Righarts
la source
7
+1 avec une remarque mineure: vous pouvez utiliser des types de données si vous placez les valeurs de types différents dans différentes tables (ainsi, pas de fichier EAV classique, mais une sorte d'amélioration). (Mais alors vient une question supplémentaire: comment connaissez-vous le type d'un nouvel attribut?)
dezso
4
Je suis d’accord, mais j’ajouterais que EAV est également une bonne approche à utiliser lorsque vous conservez une liste de choses non pertinentes sur le plan sémantique pour votre système (pas seulement sans schéma). Par exemple, un catalogue de produits en ligne dans lequel les fonctionnalités du produit doivent être stockées et répertoriées. Vous avez une liste de paires clé / valeur à régurgiter, mais le système ne sait pas ou ne se soucie pas de savoir en quoi consistent ces clés ou ces valeurs. Dans cette situation, les périls de l’AEV sont sans importance.
Joel Brown
10
@JoelBrown Vous ne vous en souciez pas MAINTENANT, mais si un VP demande au bout d'un moment de savoir combien de chemises dans le catalogue ont des boutons marrons et des cols boutonnés, ce sera une chienne de requête à écrire. EAV lui-même indique normalement un manque de planification ou de prévoyance.
JNK
2
@JoelBrown Je ne suis pas en désaccord avec le fait qu'il a une utilisation (très petite, très étroite). Mais si les informations sont susceptibles d'être interrogées de manière structurée, elles ne devraient probablement pas figurer dans EAV
JNK
4
@JoelBrown Si vos besoins professionnels ou les données que vous stockez changent, votre modèle de données devrait l'être également . Votre modèle de données ne doit pas être gravé dans la pierre. En outre, pour une base de données relationnelle, 99% des personnes utilisent EAV, leur raisonnement se résume à «je ne veux pas perdre de temps à réfléchir à la manière de stocker mes données» plutôt qu'à «Considérer tous les modèles et bases de bases de données que je connais, EAV fonctionne mieux pour cet ensemble de données ". Je répète, il existe des cas où EAV est utile (et peut-être même la «bonne» réponse), mais ils sont rares.
Simon Righarts
18

Entité Attribut Value (EAV)

Il est considéré comme un anti-modèle par beaucoup, y compris par moi.

Voici vos alternatives:

  1. utiliser l' héritage de table de base de données

  2. utiliser des données XML et des fonctions SQLXML

  3. utiliser une base de données nosql, comme HBase

Neil McGuigan
la source
3
Certainement un anti-motif pour la plupart des cas d'utilisation. Si vous avez un très petit ensemble de données et que les performances importent peu, cela peut fonctionner pour vous.
JNK
16

Dans PostgreSQL, un module très utile pour gérer les structures EAV est le module supplémentaire hstore, disponible à partir de la version 8.4. Je cite le manuel:

Ce module implémente le hstoretype de données pour stocker des ensembles de paires clé / valeur dans une seule valeur PostgreSQL. Cela peut être utile dans divers scénarios, tels que des lignes comportant de nombreux attributs rarement examinés ou des données semi-structurées. Les clés et les valeurs sont simplement des chaînes de texte.

Depuis Postgres 9.2, il existe aussi le jsontype et une foule de fonctionnalités qui vont avec (la plupart d’entre elles ont été ajoutées avec 9.3 ).

Postgres 9.4 ajoute le type de données (largement supérieur!) "JSON binaire" jsonbà la liste des options. Avec des options d'index avancées.

Erwin Brandstetter
la source
10

Si votre base de données utilise la structure EAV, il est possible d'interroger les données de différentes manières.

La réponse de @ Simon montre déjà comment effectuer une requête en utilisant plusieurs jointures.

Exemple de données utilisées:

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

Si vous utilisez un SGBDR ayant une PIVOTfonction ( SQL Server 2005+ / Oracle 11g + ), vous pouvez interroger les données de la manière suivante:

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;

Voir SQL Fiddle avec Demo

Si vous n'avez pas accès à une PIVOTfonction, vous pouvez utiliser une fonction d'agrégat avec une CASEinstruction pour renvoyer les données:

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

Voir SQL Fiddle avec Demo

Ces deux requêtes renverront des données dans le résultat:

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

C'est drôle de voir comment le modèle EAV db est critiqué et même considéré comme un "anti-motif" par certains.

En ce qui me concerne, les principaux inconvénients sont:

  • La courbe d'apprentissage est plus raide si vous vous lancez dans un projet qui utilise déjà EAV il y a longtemps. En effet, les requêtes sont difficiles car vous augmentez considérablement le nombre de jointures (et de tables), ce qui vous demandera plus de temps pour comprendre. Il suffit de jeter un coup d’œil sur le projet Magento et de voir à quel point les développeurs externes du projet ont du mal à travailler sur la base de données, alors que la documentation est bien maintenue.
  • Ne convient pas aux rapports , si vous devez connaître le nombre de personnes dont le nom a commencé par "M", etc.

Cependant, vous ne devez absolument pas écarter cette solution, et voici pourquoi:

  • Simon a parlé du monstre appelé "exigences changeantes". J'aime cette expression :). Et à mon humble avis, c’est précisément pourquoi EAV peut être un bon candidat, car il convient bien au "changement" , car vous pouvez ajouter autant d’attributs que vous le souhaitez. Bien sûr, cela dépend des exigences que nous modifions. Si nous parlons d'une toute nouvelle entreprise, vous devrez bien sûr revoir votre dataModel, mais EAV offre beaucoup de flexibilité. Ce n’est pas parce que cela demande plus de rigueur que cela est moins intéressant.
  • Il a également été dit que "Vous ne pouvez pas utiliser de types de données". : C'est faux . Vous pouvez très bien avoir plusieurs tables de valeurs , une pour chaque type de données. Vous devez ensuite spécifier dans votre table d'attribut quel type de donnée est votre attribut. En fait, un mélange de relations relationnelles / EAV classiques avec une relation de classe offre un potentiel intéressant dans la conception de bases de données.
Melvin PRESSOUYRE
la source
2
La courbe d'apprentissage est plus raide lors de la première conception d'EAV rencontrée. Après cela, tous se ressemblent.
jeudi
1
Commentaire temporaire: Je ne comprends pas pourquoi la revendication "ne convient pas à la création de rapports". EAV semble idéal pour les rapports. Sélectionnez ObjectId dans eav.values ​​où propertyId = nom et valeur comme "m%". Les modifications apportées au schéma virtuel (par exemple, l'ajout de propriétés) peuvent être incluses dans toutes les interfaces de génération de rapports dynamiques (telles que les menus déroulants) sans avoir à recompiler.
crokusek