Existe um nome para este esquema de banco de dados de valores-chave?


68

Processamos um feed de dados de rotina de um cliente que apenas refatorou seu banco de dados de um formulário que parece familiar (uma linha por entidade, uma coluna por atributo) para um que me parece desconhecido (uma linha por entidade por atributo):

Antes: uma coluna por atributo

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

Depois: uma coluna para todos os 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     ...

Existe um nome para essa estrutura de banco de dados? Quais são as vantagens relativas? A maneira antiga parece mais fácil de colocar restrições de validade em atributos específicos (não nulos, não negativos, etc.) e mais fácil de calcular médias. Mas posso ver como pode ser mais fácil adicionar novos atributos sem refatorar o banco de dados. Essa é uma maneira padrão / preferida de estruturar dados?

Respostas:


91

Chama-se Entity-Attribute-Value (também às vezes 'pares nome-valor') e é um caso clássico de "um pino redondo em um buraco quadrado" quando as pessoas usam o padrão EAV em um banco de dados relacional.

Aqui está uma lista de por que você não deve usar o EAV:

  • Você não pode usar tipos de dados. Não importa se o valor é uma data, um número ou dinheiro (decimal). Sempre será coagido a varchar. Isso pode ser qualquer coisa, desde um pequeno problema de desempenho até uma enorme dor de barriga (já teve que perseguir uma variação de um centavo em um relatório mensal de roll-up?).
  • Você não pode (facilmente) impor restrições. Requer uma quantidade ridícula de código para impor "Todo mundo precisa ter uma altura entre 0 e 3 metros" ou "A idade não deve ser nula e> = 0", em oposição às 1-2 linhas em que cada uma dessas restrições seria em um sistema adequadamente modelado.
  • Relacionado ao acima, você não pode garantir facilmente que obtém as informações necessárias para cada cliente (a idade pode estar faltando em um e a próxima pode estar perdendo a altura etc.). Você pode fazer isso, mas é muito mais difícil do que isso SELECT height, weight, age FROM Client where height is null or weight is null.
  • Relacionado novamente, os dados duplicados são muito mais difíceis de detectar (o que acontece se eles fornecerem duas idades para um cliente? A eliminação dos dados EAV, como abaixo, fornecerá duas linhas de resultados se você tiver um atributo duplicado. Se um cliente possui duas entradas separadas para dois atributos, você obterá quatro linhas da consulta abaixo).
  • Você nem pode garantir que os nomes dos atributos sejam consistentes. "Age_yr" pode se tornar "AGE_IN_YEARS" ou "age". (É certo que isso é menos problemático quando você recebe uma extração do que quando as pessoas estão inserindo dados, mas ainda assim.)
  • Qualquer tipo de consulta não trivial é um desastre completo. Para relacionalizar um sistema EAV de três atributos para que você possa consultá-lo de forma racional, são necessárias três junções da tabela 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"

Para:

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

Aqui está uma lista (muito curta) de quando você deve usar o EAV:

  • Quando não há absolutamente nenhuma maneira de contornar isso e você precisa oferecer suporte a dados sem esquema no seu banco de dados.
  • Quando você só precisa armazenar "coisas" e não espera precisar de uma forma mais estruturada. Cuidado, porém, o monstro chamado "requisitos em mudança".

Eu sei que eu só passei todo este post detalhando por EAV é uma idéia terrível na maioria dos casos - mas não são poucos os casos onde é necessária / inevitável. no entanto, na maioria das vezes (incluindo o exemplo acima), será muito mais complicado do que vale a pena. Se você precisa de um amplo suporte para entrada de dados do tipo EAV, deve armazená-los em um sistema de valores-chave, por exemplo, Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.


7
+1 com um aviso menor: você pode usar tipos de dados se colocar os valores de tipos diferentes em tabelas diferentes (bem, não o EAV clássico, mas o aprimoramento). (Mas então surge uma questão adicional: como você sabe o tipo de um novo atributo?)
Dezso

4
Concordo, mas gostaria de acrescentar que o EAV também é uma boa abordagem a ser usada quando você mantém uma lista de itens semanticamente irrelevantes para o seu sistema (não apenas sem esquema). Por exemplo, um catálogo de produtos on-line onde os recursos do produto precisam ser armazenados e listados. Você tem uma lista de pares de chave / valor para regurgitar, mas o sistema não sabe nem se importa com o que são essas chaves ou valores. Nessa situação, os perigos do EAV são irrelevantes.
Joel Brown

10
@JoelBrown Você não se importa AGORA, mas se um vice-presidente pedir para saber quantas camisas no catálogo têm botões marrons e golas, será uma pergunta difícil de escrever. O próprio EAV normalmente indica falta de planejamento ou previsão.
JNK

2
@JoelBrown Não estou discordando que ele tenha um uso (muito pequeno e muito estreito). Mas, se a informação é provável que nunca ser consultado em qualquer forma estruturada ele provavelmente não deveria estar na EAV
JNK

4
@JoelBrown Se os requisitos da sua empresa ou os dados que você está armazenando forem alterados, o modelo de dados também deve mudar . Seu modelo de dados não deve ser esculpido em pedra. Além disso, para um banco de dados relacional, 99% das vezes que as pessoas usam o EAV, seu raciocínio se resume a "Não quero gastar tempo pensando em como armazenar meus dados" em vez de "Considerando todos os padrões e modelos de banco de dados que eu conheço, O EAV funciona melhor para esse conjunto de dados ". Para repetir - não são casos em que EAV é útil (e talvez até mesmo a resposta 'certa'), mas eles são poucos e distantes entre si.
Simon Righarts


16

No PostgreSQL, uma maneira muito boa de lidar com estruturas EAV é o módulo adicional hstore, disponível para a versão 8.4 ou posterior. Cito o manual:

Este módulo implementa o hstoretipo de dados para armazenar conjuntos de pares de chave / valor em um único valor do PostgreSQL. Isso pode ser útil em vários cenários, como linhas com muitos atributos que raramente são examinados ou dados semiestruturados. Chaves e valores são simplesmente cadeias de texto.

Desde o Postgres 9.2, há também o jsontipo e uma série de funcionalidades para acompanhá-lo (a maioria foi adicionada com a 9.3 ).

O Postgres 9.4 adiciona o tipo de dados "JSON binário" (em grande parte superior!) À jsonblista de opções. Com opções avançadas de índice.


10

Se você possui um banco de dados que está usando a estrutura EAV, é possível consultar os dados de várias maneiras.

A resposta do @ Simon já mostra como executar uma consulta usando várias junções.

Dados de amostra usados:

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 você estiver usando um RDBMS com uma PIVOTfunção ( SQL Server 2005+ / Oracle 11g + ), poderá consultar os dados da seguinte maneira:

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;

Veja SQL Fiddle com demonstração

Se você não tiver acesso a uma PIVOTfunção, poderá usar uma função agregada com uma CASEinstrução para retornar os dados:

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

Veja SQL Fiddle com demonstração

Ambas as consultas retornarão dados no resultado:

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

10

Engraçado ver como o modelo EAV db é criticado e até considerado como um "anti-padrão" por alguns.

Para mim, as principais desvantagens são:

  • A curva de aprendizado é mais acentuada se você entrar em um projeto que já começou a usar o EAV há algum tempo. De fato, as consultas são difíceis à medida que você aumenta muito o número de junções (e tabelas) e, portanto, isso exige mais tempo para você entender. Basta dar uma olhada no projeto Magento e ver como os desenvolvedores externos ao projeto enfrentam dificuldades para trabalhar no banco de dados, mas a documentação é bem sustentada.
  • Não é adequado para relatórios , se você precisar obter o número de pessoas cujo nome começou com "M" etc ...

No entanto, você definitivamente não deve descartar esta solução, e aqui está o porquê:

  • Simon falou sobre o monstro chamado "requisitos em mudança". Eu gosto dessa expressão :). E IMHO, é exatamente por isso que o EAV pode ser um bom candidato, porque isso é adequado para "mudança" , pois você pode adicionar quantos atributos desejar com facilidade. Claro que depende dos requisitos que estamos mudando. Se estamos falando de um negócio totalmente novo, é claro que você terá que revisar seu dataModel, mas o EAV oferece muita flexibilidade. Só porque exige mais rigor, não significa que isso seja menos interessante.
  • Também foi dito que "você não pode usar tipos de dados". : Isso está errado . Você pode muito bem ter várias tabelas de valores , uma para cada dataType. Você precisa especificar em sua tabela de atributos qual tipo de dado é o seu atributo. De fato, uma combinação de relação clássica / EAV clássica com relação de classe oferece muito potencial interessante no design do banco de dados.

2
A curva de aprendizado é mais acentuada para o primeiro projeto de EAV encontrado. Depois disso, todos se parecem.
precisa saber é o seguinte

11
Comentário temporário: não entendo por que a reivindicação "não é adequada para denúncia". O EAV parece ótimo para relatórios. Selecione ObjectId em eav.values ​​em que propertyId = nome e valor como 'm%'. Alterações no esquema virtual (por exemplo, adição de propriedades) podem ser incluídas em qualquer interface de relatório dinâmico (como menus suspensos) sem precisar recompilar.
crokusek
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.