EAV (Entity attribute value)

Введение

Одним из желаний разработчиков баз данных всегда было отсутствие внезапных изменений структуры базы. И, увы, оно несбыточно! Совсем без этих изменений не получается. То у клиента в клиентской базе появляется дополнительное поле статуса, то маркетологи посетят курсы повышения квалификации и начинают требовать поля для SWAT-анализа прямо в карточках заказчиков. То, вы внезапно узнаёте, что команда маркетологов вновь отправилась на курсы повышения квалификации, где они скорее всего узнают о воронке продаж и именно для неё потребуются дополнительные сущности, поля и таблицы.

Изменения в процессе жизни базы данных неизбежны.

И что же с этим делать?

Самое простое решение —  это расширять таблицы и плодить новые. Но по мере появления новых «хотелок» потихоньку начинает копиться и куча хлама из старых- ненужных. И хорошо, если эти старые не тянут за собой ограничения на целостность, внешние ключи и т.п. Так как кроме существенного замедления скорости разработки это создаёт огромную бесполезную нагрузку на стену 🙂 на которой висит распечатка структуры таблиц.

EAV

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

EAV — Entity Attribute Value. Список атрибутов сущности. Она же структура в стиле Property, она же Key-Value, она же INI файл. (Если вспомните ещё названий — пишите)

Идея использования такой структуры очень проста. Вместо фиксированного набора полей таблицы вы создаёте уникальный ключ объекта (Entity) и таблицу содержащую название свойства и его значение (Attribute — Value).

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

Например, вот так может выглядеть расширение таблицы «Заказчики» с помощью структур EAV.

EAV_Structure_Sample

Вариации

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

Если вы хотите немного сэкономить место на диске, то можете в таблице атрибуты хранить не имя атрибута, а его id.

EAV_Structure_Attributes

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

Ссылки

Есть несколько очень хороших статей, посвященных EAV.

Занимательный факт! В одной из лучших статей на эту тему аббревиатура EAV вообще не используется!

ООП в РСУБД (Эта статья обязательна к прочтению!)

Немного критики

  • Только EAV и ничего больше

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

  • Типизация

Прикручивание типизации к EAV структурам задача весьма спорная. С одной стороны, что может быть проще, чем указать в табличке Attributes описания типов конкретных атрибутов. Но это легко решаемая задача только для простых типов (int, string) а для чуть более сложных уже требуется поддержка со стороны внешнего кода. Тип datatime уже хранить не так просто. Как вариант некоторые любители EAV добавляют в таблицу Properties поля всех типов, которые могут понадобиться в приложении. Или создают несколько таблиц (stringProperties, datetimeProperties и т.п.). Оба варианта действительно решают проблему типизации свойств.

Но именно в момент, когда вы победили проблему типизации, стоит задуматься — а действительно ли именно эти поля вы хотите хранить как EAV? Возможно в этот момент просто пришло время расширить основные таблицы?

  • Сложные объекты

Что может быть проще чем засунуть в EAV структуру — JSON? Не особо просто, но в принципе то реально. А с небольшими изменениями структуры (Например, добавив к таблице properties поле rootObjectId) —  это еще и работать будет довольно шустро. Для хранения объектов, которые всегда используются целиком и очень редко меняются — тоже спорный, но работоспособный вариант.

  • Иерархии

Комментарий к тебе из одного слова в стиле игры «морской бой» — «Потопил». Структуры EAV очень плохо реализуют работу с иерархиями. Вернее, с переносом и поиском ветвей дерева. Дело в том, что чрезвычайно сложно в структурах типа EAV «размотать» иерархию и поддерживать её целостность. В данном случае, поддержка со стороны БД сводится к нулю. И всё поддержание целостности структур ложится на клиентский код.

Рекомендация для иерархий в EAV — не используйте! Или если очень хочется — создайте классическую таблицу, в которой будет реализована иерархия и уже к этой таблице «пристегните» EAV.

К обсуждению

  • Каскадные операции

Со сложными структурами EAV можно реализовать один занятный трюк. Можно сделать «сборку мусора». Всё просто. Если запись об объекте существует в таблице, хранящей id (ids), то объект считается достижимым из корня иерархий. Соответственно, удаление объекта из этой таблицы — делает объект «потерянным», т.е. недостижимым с корня. Что позволяет реализовать стратегию удаления объектов через удаление объекта из корня и специального метода, удаляющего записи не достижимые из корня. Естественно, для поддержания такой структуры потребуется расширение списка атрибутов для поддержки атрибута-ссылки на объект.

В качестве академической задачи — это великолепная возможность. Но на практике «сборка мусора» в БД выглядит странно. Хотя, вполне допускаю, что в конкретных случаях такая стратегия может дать свои преимущества.

  • EAV и SoftDelete

Когда говорят о SoftDelete (пометке объекта как удалённого вместо физического удаления) то применительно к EAV есть как минимум 2.5 способа реализации.

Первый — расширить таблицу ids, добавив в неё атрибут Deleted.

Второй — создать новый bool атрибут Deleted.

Оба варианта имеют права на существование и обсуждении.

Связанные вопросы

  • Иерархии

Один из уже упоминавшихся в связи с EAV вопросов — это вопрос хранения иерархий. Да, EAV не очень хорошо подходит для хранения иерархий в классической (через поле Parent) схеме. Но кто сказал, что эта схема единственная?

В схему хранения EAV довольно хорошо вписывается хранения иерархий через «материализованный путь». Если описать это в двух словах — то храните иерархию в виде, развёрнутом в цепочку. Например, если у вас есть структура товарного каталога: «Root\Еда\Кондитерские изделия\Пирожные\Эклеры\», то именно эту строку и можно хранить в качестве Value атрибута Path. Если кого-нибудь смущает хранение длинных строк — то можно сократить запись, создав словарь с названиями папок. И превратив запись:

«Root\Еда\Кондитерские изделия\Пирожные\Эклеры\»

в более лаконичную

«0\1\2\3\4\»

Интересные возможности

  • Декларативное построение интерфейса

Расширив таблицу описания атрибутов вполне можно реализовать логику автогенерации интерфейса по данным. Для простых полей, типа логических — вывод чекбокса в интерфейсе для заполнения соответствующего поля выглядит вполне логичным. Заполнение даты — тоже легко. Заполнение id для поля-ссылки на справочник тоже сделать реально (реализацию можно подсмотреть в метаданных таблиц, которые генерируем MS Access в режиме работы с MS SQL)   Единственный вопрос — в каком именно месте стоит остановиться и предпочесть явную реализацию интерфейса автоматической. Где находится именно там точка до которой автоматизировать «авто-интерфейс» надо, а где стоит заняться проектированием интерфейса руками?

Вопрос автопостроения интерфейсов по описанию данных очень интересен. Кончено же тут есть и подводные камни. Но от этого задача автоматической генерации интерфейса не становиться менее интересной.

Выводы и рекомендации

Не используете EAV в чистом виде. (Исключение — учебные проекты). Если нужны «пользовательские» атрибуты, или есть предпосылки для частого и хаотичного изменения структуры данных — «пристегните» EAV к существующим таблицам но не заменяйте их.

P.S.

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

 

Реклама

Об авторе DimonSmart

C# developer, researcher, MS SQL Server Developer Hobbies: Design, RC models, 3D-printing, Robotics
Запись опубликована в рубрике Programming, SQL с метками , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google photo

Для комментария используется ваша учётная запись Google. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s