¿Hay un nombre para este esquema de base de datos de valores clave?

68

Procesamos una fuente de datos de rutina de un cliente que acaba de refactorizar su base de datos de un formulario que parece familiar (una fila por entidad, una columna por atributo) a uno que no me resulta familiar (una fila por entidad por atributo):

Antes: una columna por atributo

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

Después: una columna para todos los atributos

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

¿Hay un nombre para esta estructura de base de datos? ¿Cuáles son las ventajas relativas? La forma antigua parece más fácil de imponer restricciones de validez a atributos específicos (no nulos, no negativos, etc.) y más fácil de calcular promedios. Pero puedo ver cómo podría ser más fácil agregar nuevos atributos sin refactorizar la base de datos. ¿Es esta una forma estándar / preferida de estructurar datos?

prototipo
fuente

Respuestas:

91

Se llama Entity-Attribute-Value (también a veces 'pares de nombre-valor') y es un caso clásico de "una clavija redonda en un agujero cuadrado" cuando las personas usan el patrón EAV en una base de datos relacional.

Aquí hay una lista de por qué no debe usar EAV:

  • No puedes usar tipos de datos. No importa si el valor es una fecha, un número o dinero (decimal). Siempre será forzado a varchar. Esto puede ser cualquier cosa, desde un problema de rendimiento menor hasta un dolor de intestino masivo (¿alguna vez tuvo que perseguir una variación de un centavo en un informe acumulativo mensual?).
  • No puede (fácilmente) imponer restricciones. Se requiere una cantidad ridícula de código para imponer "Todos deben tener una altura entre 0 y 3 metros" o "La edad no debe ser nula y> = 0", a diferencia de las líneas 1-2 que cada una de esas restricciones sería en un sistema modelado adecuadamente.
  • En relación con lo anterior, no puede garantizar fácilmente que obtenga la información que necesita para cada cliente (es posible que falte la edad de uno, luego el siguiente puede perder su altura, etc.). Usted puede hacerlo, pero es un infierno de mucho más difícil de lo SELECT height, weight, age FROM Client where height is null or weight is null.
  • Relacionado de nuevo, la duplicación de datos es mucho más difícil de detectar (¿qué sucede si le dan dos edades para un cliente? Eliminar los datos, como se muestra a continuación, le dará dos filas de resultados si tiene un atributo duplicado. Si un cliente tiene dos entradas separadas para dos atributos, obtendrá cuatro filas de la consulta a continuación).
  • Ni siquiera puede garantizar que los nombres de los atributos sean consistentes. "Age_yr" podría convertirse en "AGE_IN_YEARS" o "age". (Es cierto que esto no es un problema cuando recibes un extracto en comparación con cuando la gente está insertando datos, pero aún así).
  • Cualquier tipo de consulta no trivial es un completo desastre. Para relacionalizar un sistema EAV de tres atributos para que pueda consultarlo de manera racional, se requieren tres combinaciones de la tabla EAV.

Comparar:

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"

A:

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

Aquí hay una lista (muy corta) de cuándo debe usar EAV:

  • Cuando no hay absolutamente ninguna manera de evitarlo y hay que apoyar a los datos sin esquema en su base de datos.
  • Cuando solo necesita almacenar "cosas" y no espera tener que necesitarlas de una forma más estructurada. Tenga cuidado, sin embargo, el monstruo llamado "requisitos cambiantes".

Sé que acabo de pasar todo este post detallando qué EAV es una idea terrible en la mayoría de los casos - pero no son unos pocos casos donde se necesita / inevitable. sin embargo, la mayoría de las veces (incluido el ejemplo anterior), será mucho más complicado de lo que vale. Si necesita un amplio soporte para la entrada de datos de tipo EAV, debería considerar almacenarlos en un sistema de valores clave, por ejemplo, Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.

Simon Righarts
fuente
77
+1 con un aviso menor: puede usar tipos de datos si coloca los valores de diferentes tipos en diferentes tablas (bueno, no EAV clásico, sino una especie de mejora). (Pero luego viene una pregunta adicional: ¿cómo sabes el tipo de un nuevo atributo?)
dezso
44
De acuerdo, pero agregaría que EAV también es un buen enfoque para usar cuando mantiene una lista de cosas que son semánticamente irrelevantes para su sistema (no solo sin esquema). Por ejemplo, un catálogo de productos en línea donde las características del producto deben almacenarse y enumerarse. Tiene una lista de pares clave / valor para regurgitar, pero el sistema en realidad no sabe ni le importa de qué se tratan esas claves o valores. En esa situación, los peligros de EAV son irrelevantes.
Joel Brown
10
@JoelBrown No te importa AHORA, pero si en el futuro un vicepresidente pide saber cuántas camisas en el catálogo tienen botones marrones y cuellos de botones, será una pregunta difícil de escribir. El EAV en sí mismo normalmente indica una falta de planificación o previsión.
JNK
2
@JoelBrown No estoy en desacuerdo con que tiene un uso (muy pequeño, muy estrecho). Pero si es probable que la información sea consultada de alguna manera estructurada, probablemente no debería estar en EAV
JNK
44
@JoelBrown Si sus requisitos comerciales o los datos que está almacenando cambian, también debería cambiar su modelo de datos . Su modelo de datos no debe estar tallado en piedra. Además, para una base de datos relacional, el 99% del tiempo que las personas usan EAV su razonamiento se reduce a "No quiero pasar tiempo pensando en cómo almacenar mis datos" en lugar de "Considerando todos los patrones y modelos de bases de datos que conozco, EAV funciona mejor para este conjunto de datos ". Para repetir - no son casos en los EAV es útil (y tal vez incluso la respuesta 'correcta'), pero son pocos y distantes entre sí.
Simon Righarts
18

Valor de atributo de entidad (EAV)

Muchos lo consideran, incluso yo, un antipatrón.

Aquí están tus alternativas:

  1. usar herencia de tabla de base de datos

  2. usar datos XML y funciones SQLXML

  3. usar una base de datos nosql, como HBase

Neil McGuigan
fuente
3
Definitivamente un antipatrón para la mayoría de los casos de uso. Si tiene un conjunto de datos realmente pequeño y el rendimiento no importa, puede funcionar para usted.
JNK
16

En PostgreSQL, una muy buena manera de lidiar con las estructuras EAV es el módulo adicional hstore, disponible para la versión 8.4 o posterior. Cito el manual:

Este módulo implementa el hstoretipo de datos para almacenar conjuntos de pares clave / valor dentro de un único valor PostgreSQL. Esto puede ser útil en varios escenarios, como filas con muchos atributos que rara vez se examinan o datos semiestructurados. Las claves y los valores son simplemente cadenas de texto.

Desde Postgres 9.2 también existe el jsontipo y una gran cantidad de funcionalidades (la mayoría se agrega con 9.3 ).

Postgres 9.4 agrega el tipo de datos (¡en gran medida superior!) "JSON binario" jsonba la lista de opciones. Con opciones de índice avanzadas.

Erwin Brandstetter
fuente
10

Si tiene una base de datos que utiliza la estructura EAV, es posible consultar los datos de varias maneras.

La respuesta de @ Simon ya muestra cómo realizar una consulta utilizando múltiples combinaciones.

Datos de muestra utilizados:

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 está utilizando un RDBMS que tiene una PIVOTfunción ( SQL Server 2005+ / Oracle 11g + ), puede consultar los datos de la siguiente manera:

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;

Ver SQL Fiddle con Demo

Si no tiene acceso a una PIVOTfunción, puede usar una función agregada con una CASEdeclaración para devolver los datos:

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

Ver SQL Fiddle con Demo

Ambas consultas devolverán datos en el resultado:

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

Es divertido ver cómo el modelo EAV db es criticado e incluso considerado como un "antipatrón" por algunos.

En lo que a mí respecta, las principales desventajas son:

  • La curva de aprendizaje es más pronunciada si te subes a un proyecto que ya comenzó a usar EAV hace un tiempo. De hecho, las consultas son difíciles, ya que aumenta considerablemente el número de uniones (y tablas) y, por lo tanto, le pedirá más tiempo para que lo entienda. Solo eche un vistazo al proyecto Magento y vea cómo los desarrolladores externos al proyecto tienen dificultades para trabajar en la base de datos, aunque la documentación está bien sostenida.
  • No es adecuado para informar , si necesita obtener el número de personas cuyo nombre comenzó con "M", etc.

Sin embargo, definitivamente no debe descartar esta solución, y esta es la razón:

  • Simon habló sobre el monstruo llamado "requisitos cambiantes". Me gusta esta expresión :). Y en mi humilde opinión, esta es precisamente la razón por la cual EAV puede ser un buen candidato, porque es muy adecuado para el "cambio" , ya que puede agregar tantos atributos como desee con bastante facilidad. Por supuesto, depende de los requisitos que estamos cambiando. Si hablamos de un negocio completamente nuevo, por supuesto, tendrá que revisar su modelo de datos, pero EAV ofrece mucha flexibilidad. El hecho de que pida más rigor no significa que sea menos interesante.
  • También se dijo que "No se pueden usar tipos de datos". : Esto está mal . Es muy posible que tenga varias tablas de valores , una para cada tipo de datos. Luego debe especificar en su tabla de atributos qué tipo de datos es su atributo. De hecho, una combinación de relación clásica / EAV con relación de clase ofrece un gran potencial interesante en el diseño de la base de datos.
Melvin PRESSOUYRE
fuente
2
La curva de aprendizaje es más pronunciada para el primer diseño EAV que uno encuentra. Después de eso, todos se parecen.
ypercubeᵀᴹ
1
Comentario temporal: no entiendo por qué el reclamo "no es adecuado para informar". EAV parece genial para informar. Seleccione ObjectId de eav.values ​​donde propertyId = nombre y valor como 'm%'. Los cambios en el esquema virtual (por ejemplo, agregar propiedades) se pueden incluir en cualquier interfaz de informes dinámicos (como menús desplegables) sin tener que volver a compilar.
crokusek