EAV - это действительно плохо во всех сценариях?


65

Я подумываю использовать модель сущность-атрибут-значение (EAV) для некоторых вещей в одном из проектов, но все вопросы об этом в Stack Overflow заканчиваются ответами, называющими EAV анти-паттерном.

Но мне интересно, так ли это во всех случаях?

Допустим, сущность продукта магазина, у него есть общие черты, такие как имя, описание, изображение и цена, которые во многих местах участвуют в логике, и (полу) уникальные особенности, такие как часы и пляжный мяч, будут описаны совершенно по-разному. Поэтому я думаю, что EAV подойдет для хранения этих (полу) уникальных функций.

Все это предполагает, что для отображения списка товаров достаточно информации в таблице товаров (это означает, что EAV не задействован) и просто при показе одного товара / при сравнении до 5 товаров / и т. Д. данные сохраняются с использованием EAV.

Я видел такой подход в Magento Commerce, и он довольно популярен, так есть ли случаи, когда EAV является разумным?


2
@busy_wait Таблицы "Entity-Attibute-Value" - см. модель Entity-attribute-value в Википедии .
Росс Паттерсон

Для примера того, как шаблон EAV работает очень хорошо, взгляните на базу данных Datomic. Он хранит все в шаблоне EAVT (T - это «временная метка», фактически больше похожая на идентификатор транзакции). Их [документация по индексированию] (docs.datomic.com/indexes.html), кажется, показывает это лучше всего. Для примера EAV, работающего ужасно, см. Wordpress .
Дэн Росс

Ответы:


81

https://web.archive.org/web/20140831134758/http://www.dbforums.com/database-concepts-design/1619660-otlt-eav-design-why-do-people-hate.html

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

С другой стороны, он работает очень плохо в случае плохо определенного запроса и может поддерживать другие плохие методы.

Другими словами, EAV дает вам достаточно веревки, чтобы повеситься, и в этой отрасли все должно быть разработано с наименьшим уровнем сложности, потому что парень, заменяющий вас в проекте, скорее всего, будет идиотом.


32
Люблю последнее предложение.
Зохар Пелед

2
Гнилая ссылка. Кэшированная версия где-нибудь есть?
Wildcard

1
Не переходите по ссылке. Страница загружается медленно и не помогает. Кроме того, старые форумы воняют. Вместо этого используйте переполнение стека! Поднимите хорошие / полезные ответы и выбросьте мусор.
Джесс

29

В двух словах, EAV полезен, когда ваш список атрибутов часто увеличивается, или когда он настолько большой, что большинство строк будут заполнены в основном значениями NULL, если вы сделаете каждый атрибут столбцом. Это становится анти-паттерном, когда используется вне этого контекста.


16
Я бы заменил «часто» на «необходимо изменить во время выполнения».
Док Браун

3
Мы можем сократить это еще больше, используя Док Браун, используя довольно хорошо понятное слово «динамический» - EAV полезен, когда ваш список атрибутов может изменяться динамически.
Александр Миллс

Еще дальше: «когда ваши атрибуты могут измениться» - «динамически» в этом контексте немного избыточно :)
Wranorn

1
Обязательно ли это более полезно, чем, скажем, иметь форму для изменения атрибута, выполняющую a CREATE TABLEдля нового атрибута?
Дамиан Йеррик

@DamianYerrick интересный подход. Вы использовали это в производстве?
выезд

21

Скажем, сущность продукта магазина, у него есть общие черты, такие как имя, описание, изображение, цена и т. Д., Которые участвуют в логической практике во многих местах, и (полу) уникальные черты, такие как часы и пляжный мяч, могут быть описаны совершенно по-разному , Так что я думаю, что EAV подойдет для хранения этих (полу) уникальных функций?

Использование структуры EAV для имеет несколько последствий, которые являются компромиссами.

Вы торгуете «меньшим пространством для строки, потому что у вас нет 100 столбцов, которые null« против »более сложных запросов и модели».

Наличие EAV обычно означает, что значение является строкой, в которую можно вставить любые данные. Это имеет значение для проверки правильности и ограничения. Рассмотрим ситуацию, когда вы указали количество используемых батарей в таблице EAV. Вы хотите найти фонарик, который использует батареи размера С, но менее 4 из них.

select P.sku
from
  products P
  attrib Ab on (P.sku = Ab.sku and Ab.key = "batteries")
  attrib Ac on (P.sku = Ac.sku and Ac.key = "count")
where
  cast(Ac.value as int) < 4
  and Ab.value = 'C'
  ...

Здесь нужно понять, что вы не можете разумно использовать индекс для значения. Вы также не можете запретить кому-либо вставлять что-то, что не является целым числом или недопустимым целым числом (использует батарейки -1), поскольку столбец значений используется снова и снова для различных целей.

Это имеет значение при попытке написать модель для продукта. У вас будут хорошие напечатанные значения ... но вы также будете Map<String,String>просто сидеть там со всеми видами вещей в нем. Это затем имеет дополнительные последствия при сериализации его в XML или Json и сложности попыток проверки или запросов к этим структурам.

Некоторые альтернативы или модификации рассматриваемого шаблона вместо ключа свободной формы должны иметь другую таблицу с допустимыми ключами. Это означает, что вместо сравнения строк в базе данных вы проверяете равенство идентификаторов внешних ключей. Смена самого ключа производится в одном месте. У вас есть известный набор ключей, что означает, что они могут быть выполнены как enum.

Вы также можете иметь связанные таблицы, которые содержат атрибуты определенного класса продукта. В продуктовом отделе может быть другая таблица, с которой связано несколько атрибутов, которые не нужны строительным материалам (и наоборот).

+----------+    +--------+    +---------+
|Grocery   |    |Product |    |BuildMat |
|id (fk)   +--->|id (pk) |<---+id (fk)  |
|expiration|    |desc    |    |material |
|...       |    |img     |    |...      |
+----------+    |price   |    +---------+
                |...     |               
                +--------+               

Есть моменты, которые особенно требуют таблицы EAV.

Рассмотрим ситуацию, когда вы не просто пишете систему инвентаризации для своей компании, где вы знаете каждый продукт и каждый атрибут. Вы сейчас пишете систему инвентаризации для продажи другим компаниям. Вы не можете знать каждый атрибут каждого продукта - они должны будут определить их.

Одна идея, которая выходит: «мы позволим клиенту модифицировать таблицу», и это просто плохо (вы попадаете в метапрограммирование для структур таблиц, потому что вы больше не знаете, что, где, они могут по- королевски испортить структуру или испортить приложение, у них есть доступ, чтобы делать неправильные вещи, и последствия этого доступа становятся значительными). Есть больше об этом пути в MVC4: Как создать модель во время выполнения?

Вместо этого вы создаете административный интерфейс для таблицы EAV и разрешаете его использовать. Если клиент хочет создать запись для «polkadots», он попадает в таблицу EAV, и вы уже знаете, как с этим справиться.

Пример этого можно увидеть в модели базы данных для Redmine, где вы можете увидеть таблицу custom_fields и таблицу custom_values ​​- это те части EAV, которые позволяют расширять систему.


Обратите внимание, что если вы обнаружите, что вся структура вашей таблицы выглядит как EAV, а не как реляционная, вы, возможно, захотите взглянуть на разновидность KV NoSQL (cassandra, redis, Mongo, ... ...). Поймите, что они часто идут с другими компромиссами в их дизайне, которые могут или не могут соответствовать тому, для чего вы его используете. Тем не менее, они специально разработаны с целью структуры EAV.

Вы можете прочитать SQL против NoSQL для системы управления запасами

Следуя такому подходу с базой данных NoSQL, ориентированной на документы (couch, mongo), вы можете рассматривать каждый элемент инвентаря как документ на диске ... быстро собрать все в одном документе. Кроме того, документ структурирован так, что вы можете быстро извлечь что-то одно. С другой стороны, поиск во всех документах вещей, которые соответствуют определенному атрибуту, может иметь меньшую производительность (сравните, используя 'grep' со всеми файлами) ... все это компромисс.

Другим подходом может быть LDAP, где у каждого будет база со всеми связанными с ней элементами, но затем к нему будут применены дополнительные классы объектов для других типов элементов. (см. Инвентаризация системы с использованием LDAP )

Как только вы пойдете по этому пути, вы можете найти что-то, что точно соответствует тому, что вы ищете ... хотя все идет с некоторыми компромиссами.


10

6 лет спустя

Теперь, когда JSON в Postgres здесь, у нас есть еще один вариант для тех, кто использует Postgres. Если вы хотите прикрепить к продукту только некоторые дополнительные данные, тогда ваши потребности довольно просты. Пример:

CREATE TABLE products (sku VARCHAR(30), shipping_weight REAL, detail JSON);
INSERT INTO products ('beachball', 1.0, '{"colors": ["red", "white"], "diameter": "50cm"}');

SELECT * FROM products;
    sku    | weight |               detail               
-----------+--------+------------------------------------
 beachball |      1 | {"colors": ["red", "white"], "diameter": "50cm"}

Вот более плавное введение в JSON в Postgres: https://www.compose.com/articles/is-postgresql-your-next-json-database/ .

Обратите внимание, что Postgres на самом деле хранит JSONB, а не обычный текст JSON, и он поддерживает индексы для полей внутри документа / поля JSONB, если вы обнаружите, что действительно хотите запросить эти данные.

Также обратите внимание, что поля внутри поля JSONB не могут быть изменены индивидуально с помощью запроса UPDATE; вам придется заменить все содержимое поля JSONB.

Этот ответ не может напрямую касаться вопроса, но он предлагает альтернативу шаблону EAV, который должен рассмотреть каждый, кто размышляет над первоначальным вопросом.


3
Я думаю, что это хорошая идея, чтобы опубликовать альтернативное решение. Просто чтобы держать других в курсе, MS SQL поддерживал столбцы XML с возможностью их индексации некоторое время, и начиная с 2016 года он может делать то же самое с JSON (хотя JSON не является собственным типом столбцов в MS SQL, вы все равно можете индексировать его ). С другой стороны - из того, что я прочитал, лучше поддержка Postgres JSON, например, похоже, что он поддерживает индексы данных в свойствах массива JSON.
Гиедрюс

1
«... поля внутри поля JSONB не могут быть изменены индивидуально с помощью запроса UPDATE; вам придется заменить все содержимое поля JSONB». Это устарело, не так ли? В jsonb_set()Postgres 9.5 и более поздних версиях есть функция, предназначенная именно для этого. (Статья, на которую вы ссылались по ссылкам, в свою очередь, относится к новой статье, в которой обсуждаются дополнения к 9.5 .)
Подстановочный

7

Обычно люди смотрят иначе, если вы используете их для поиска таблиц, или в других ситуациях, когда преимущество состоит в том, чтобы не создавать таблицы для одного или двух сохраненных значений. Ситуация, которую вы описываете, когда вы в основном храните свойства элемента, звучит совершенно нормально (и нормализуется). Расширение таблицы для хранения переменного количества атрибутов элементов - плохая идея.

Для общего случая хранения разрозненных данных в длинной тонкой таблице ... Вы не должны бояться создавать новые таблицы, если вам нужно, и иметь только одну или две длинные тонкие таблицы не намного лучше, чем иметь только одну или две короткие толстые таблицы.

Это, как говорится, я известен тем, что использовал таблицы EAV для регистрации. У них есть хорошая полезность.


Пожалуйста, определите "тощий стол" и "толстый стол".
Тулаинс Кордова

@ TulainsCórdova: «тощая» таблица - это таблица с несколькими строками и множеством столбцов, тогда как толстая таблица - это таблица с множеством столбцов и несколькими строками. Примером может служить создание справочной таблицы, где у вас есть свойства, скажем, для книг. Толстая таблица будет иметь одну запись на книгу с множеством столбцов для определенных фрагментов данных, тогда как в толстой таблице может быть четыре столбца id, book, field_name, field_data. Преимущество первого состоит в том, что записей меньше, но отрицательным является то, что некоторые поля могут быть пустыми, и все сложнее расширить.
Satanicpuppy

@Satanicpuppy Я думаю, что ваши тощие / толстые определения перепутаны - они одинаковы. Вы имеете в виду, что тощая таблица имеет мало столбцов и много строк?
Чарльз Вуд

1

EAV изменяет проблему явной структуры, подразумеваемого восприятия. Вместо того, чтобы говорить, что X - это таблица со столбцами A и B. Вы подразумеваете, что столбцы A и B образуют таблицу X. В некотором смысле все наоборот, но не обязательно однозначное отображение, обязательно. Можно сказать, что A и B отображаются на таблицу (или тип) X и Y. Это может быть важно в более сложной области, где имеет значение контекст.

Я изучал Datomic, для такого типа подхода, и я думаю, что это очень полезная и мощная система с ограничениями на то, что вы должны с ней делать (не то, что вы не могли).

То, что EAV будет медленным или «даст вам достаточно веревки, чтобы повеситься», - это не утверждение, с которым я бы согласился. Скорее, я бы уделил больше внимания сильным сторонам EAV, и, если это подходит вашему проблемному пространству, вы должны рассмотреть его.

Мой опыт показывает, что это замечательный, почти без ограничений подход к моделированию. В частности, в случае с Datomic они навязывают набор семантики поверх всего. Любое решение по моделированию, которое моделирует отношения, может свободно переходить от одного ко многим без необходимости перепроектировать столбцы / таблицы. Вы также можете вернуться назад, если ограничение не нарушает инвариант. Под капотом все тоже самое.

На мой взгляд, проблема с EAV заключалась в отсутствии такой реализации, как Datomic. Так как это вопрос о EAV, я не хочу в восторге от Datomic, но это одна из тех вещей, где, я думаю, они все поняли относительно EAV.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.