Есть ли имя для этой схемы базы данных значений ключей?

69

Мы обрабатываем обычную подачу данных от клиента, который просто реорганизовал свою базу данных из формы, которая кажется знакомой (одна строка на сущность, один столбец на атрибут), в форму, которая кажется мне незнакомой (одна строка на сущность на атрибут):

До: один столбец на атрибут

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

После: один столбец для всех атрибутов

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

Есть ли название для этой структуры базы данных? Каковы относительные преимущества? Старый способ кажется более простым для наложения ограничений валидности на определенные атрибуты (ненулевые, неотрицательные и т. Д.) И для вычисления средних значений. Но я вижу, как проще добавить новые атрибуты без рефакторинга базы данных. Это стандартный / предпочтительный способ структурирования данных?

прототип
источник

Ответы:

92

Он называется Entity-Attribute-Value (также иногда «пары имя-значение») и является классическим случаем «круглого колышка в квадратном отверстии», когда люди используют шаблон EAV в реляционной базе данных.

Вот список того, почему вы не должны использовать EAV:

  • Вы не можете использовать типы данных. Не имеет значения, является ли значение датой, числом или деньгами (десятичными). Это всегда будет принуждено к varchar. Это может быть что угодно, от незначительной проблемы с производительностью до огромной боли в кишечнике (когда-либо приходилось гоняться за отклонением в один цент в ежемесячном сводном отчете?).
  • Вы не можете (легко) применять ограничения. Требуется смешной объем кода для обеспечения того, что «Каждый должен иметь высоту от 0 до 3 метров» или «Возраст должен быть не нулевым, а> = 0», в отличие от 1-2 строк, которые будут обозначать каждое из этих ограничений. в правильно смоделированной системе.
  • В связи с вышесказанным, вы не можете легко гарантировать, что вы получаете необходимую информацию для каждого клиента (возраст может отсутствовать у одного, тогда у следующего может отсутствовать их рост и т. Д.). Вы можете сделать это, но это намного сложнее, чем SELECT height, weight, age FROM Client where height is null or weight is null.
  • Еще раз, связанные с дублирующимися данными обнаружить намного сложнее (что произойдет, если они дадут вам два возраста для одного клиента? Удаление данных из EAV, как показано ниже, даст вам две строки результатов, если один атрибут удвоен. Если один клиент имеет две отдельные записи для двух атрибутов, вы получите четыре строки из запроса ниже).
  • Вы даже не можете гарантировать, что имена атрибутов совпадают. "Age_yr" может стать "AGE_IN_YEARS" или "age". (Правда, это меньше проблем, когда вы получаете извлечение по сравнению с тем, когда люди вставляют данные, но все же.)
  • Любой нетривиальный запрос - это полная катастрофа. Чтобы реляционизировать систему EAV с тремя атрибутами, чтобы вы могли запросить ее рациональным способом, требуется три соединения таблицы EAV.

Для сравнения:

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

Вот (очень короткий) список того, когда вы должны использовать EAV:

  • Когда нет абсолютно никакого способа обойти это, и вам нужно поддерживать данные без схемы в вашей базе данных.
  • Когда вам просто нужно хранить «вещи» и не ожидать, что они понадобятся в более структурированной форме. Остерегайтесь, однако, монстра под названием «изменяющиеся требования».

Я знаю , что я только что провел весь этот пост подробно , почему EAV страшная мысль в большинстве случаев - но там есть несколько случаев , когда это нужно / неизбежными. однако, в большинстве случаев (включая приведенный выше пример), это будет намного сложнее, чем стоит. Если у вас есть потребность в широкой поддержке ввода данных типа EAV, вам следует рассмотреть возможность их хранения в системе ключ-значение, например, Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.

Саймон Ригартс
источник
7
+1 с небольшим уведомлением: вы можете использовать типы данных, если вы помещаете значения разных типов в разные таблицы (ну, не классический EAV, а своего рода улучшение). (Но тогда возникает дополнительный вопрос: откуда вы знаете тип нового атрибута?)
Дезсо
4
Согласен, но я хотел бы добавить, что EAV также является хорошим подходом для использования, когда вы ведете список вещей, которые семантически не имеют отношения к вашей системе (не только без схемы). Например, онлайн-каталог продукции, где функции продукта должны быть сохранены и перечислены. У вас есть список пар ключ / значение для регургитации, но система на самом деле не знает и не заботится о том, что означают эти ключи или значения. В этой ситуации опасности EAV не имеют значения.
Джоэл Браун
10
@JoelBrown Тебе все равно СЕЙЧАС, но если в будущем вице-президент попросит узнать, сколько рубашек в каталоге имеют коричневые пуговицы и воротники на пуговицах, это будет сущий запрос для написания. Сам EAV обычно указывает на отсутствие планирования или предвидения.
JNK
2
@JoelBrown Я не согласен с тем, что он имеет (очень маленький, очень узкий) вид использования. Но если информация, вероятно, когда-либо будет запрошена каким-либо структурированным способом, она, вероятно, не должна быть в EAV
JNK
4
@JoelBrown Если ваши бизнес-требования или данные, которые вы храните, изменяются, то же самое касается и вашей модели данных . Ваша модель данных не должна быть вырезана в камне. Кроме того, для реляционной базы данных 99% времени, когда люди используют EAV, сводятся к тому, что «я не хочу тратить время на размышления о том, как хранить мои данные», а не «учитывая все известные мне шаблоны и модели базы данных, EAV работает лучше всего для этого набора данных ". Повторим - там есть случаи , когда EAV полезна (и , возможно , даже «правильный» ответ), но они мало , и далеко друг от друга.
Саймон Райгартс
18

Значение атрибута сущности (EAV)

Это считается анти-паттерном для многих, в том числе и для меня.

Вот ваши альтернативы:

  1. использовать наследование таблицы базы данных

  2. использовать данные XML и функции SQLXML

  3. используйте базу данных nosql, такую ​​как HBase

Нил Макгиган
источник
3
Определенно анти-шаблон для большинства случаев использования. Если у вас действительно небольшой набор данных и производительность не имеет значения, это может сработать для вас.
JNK
16

В PostgreSQL очень хорошим способом работы со структурами EAV является дополнительный модуль hstore, доступный для версии 8.4 или новее. Я цитирую руководство:

Этот модуль реализует hstoreтип данных для хранения наборов пар ключ / значение в пределах одного значения PostgreSQL. Это может быть полезно в различных сценариях, таких как строки с множеством редко проверяемых атрибутов, или полуструктурированные данные. Ключи и значения - это просто текстовые строки.

Начиная с Postgres 9.2 есть также jsonтип и множество функциональных возможностей ( большинство из них добавлено в 9.3 ).

Postgres 9.4 добавляет (в основном превосходящий!) Тип данных «двоичный JSON» jsonbв список опций. С расширенными параметрами индекса.

Эрвин Брандштеттер
источник
10

Если у вас есть база данных, которая использует структуру EAV, можно запросить данные различными способами.

Ответ Саймона уже показывает, как выполнить запрос, используя несколько соединений.

Пример используемых данных:

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

Если вы используете СУБД, которая имеет PIVOTфункцию ( SQL Server 2005+ / Oracle 11g + ), вы можете запросить данные следующим образом:

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;

Смотрите SQL Fiddle с демо

Если у вас нет доступа к PIVOTфункции, вы можете использовать агрегатную функцию с CASEоператором для возврата данных:

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

Смотрите SQL Fiddle с демо

Оба этих запроса вернут данные в результате:

| ID | HT_CM | WT_KG | AGE_YR |
-------------------------------
|  1 |   190 |    82 |     43 |
|  2 |   170 |    60 |     22 |
|  3 |   205 |    90 |     51 |
Тарын
источник
10

Забавно видеть, как модель EAV db подвергается критике и даже рассматривается некоторыми как «анти-паттерн».

Насколько я понимаю, основные недостатки :

  • Кривая обучения круче, если вы попадаете в проект, который уже начал использовать EAV некоторое время назад. Действительно, запросы сложны, поскольку вы значительно увеличиваете количество объединений (и таблиц), и поэтому вам потребуется больше времени для понимания. Достаточно взглянуть на проект Magento и посмотреть, как внешнему по отношению к проекту разработчику сложно работать с БД, но документация хорошо поддерживается.
  • Не подходит для отчетов , если вам нужно, чтобы количество людей, чье имя начиналось с буквы "M" и т. Д ...

Однако вы не должны отказываться от этого решения, и вот почему:

  • Саймон говорил о монстре под названием «меняющиеся требования». Мне нравится это выражение :). И ИМХО именно поэтому EAV может быть хорошим кандидатом, потому что это хорошо подходит для «изменений» , так как вы можете легко добавить столько атрибутов, сколько пожелаете. Конечно, это зависит от требований, которые мы меняем. Если мы говорим о совершенно новом бизнесе, конечно, вам придется пересмотреть свою модель данных, но EAV предлагает большую гибкость. То, что он требует большей строгости, не означает, что это менее интересно.
  • Также было сказано, что «Вы не можете использовать типы данных». : Это неправильно . У вас вполне может быть несколько таблиц значений , по одной для каждого типа данных. Затем вы должны указать в своей таблице атрибутов, какой тип dataType является вашим атрибутом. На самом деле, сочетание классической реляционной связи / EAV с классовой связью предлагает большой интересный потенциал в разработке базы данных.
Мелвин ПРЕССУЙР
источник
2
Кривая обучения круче для первого дизайна EAV. После этого все выглядят одинаково.
ypercubeᵀᴹ
1
Временный комментарий: я не понимаю, почему заявление «не подходит для отчетности». EAV отлично подходит для отчетности. Выберите ObjectId из eav.values, где propertyId = имя и значение, подобное 'm%'. Изменения в виртуальной схеме (например, добавление свойств) могут быть включены в любые интерфейсы динамической отчетности (например, раскрывающиеся списки) без перекомпиляции.
Crokusek