У вас есть по крайней мере пять вариантов моделирования иерархии типов, которые вы описываете:
Наследование одной таблицы : одна таблица для всех типов продуктов с достаточным количеством столбцов для хранения всех атрибутов всех типов. Это означает много столбцов, большинство из которых имеют значение NULL в любой данной строке.
Наследование таблиц классов : одна таблица для продуктов, в которой хранятся атрибуты, общие для всех типов продуктов. Затем по одной таблице для каждого типа продукта с сохранением атрибутов, специфичных для данного типа продукта.
Наследование бетонной таблицы : нет таблицы для общих атрибутов Products. Вместо этого по одной таблице для каждого типа продукта хранятся как общие атрибуты продукта, так и атрибуты, специфичные для продукта.
Serialized LOB : одна таблица для продуктов, в которой хранятся атрибуты, общие для всех типов продуктов. В одном дополнительном столбце хранится большой двоичный объект полуструктурированных данных в формате XML, YAML, JSON или в другом формате. Этот BLOB-объект позволяет хранить атрибуты, специфичные для каждого типа продукта. Вы можете использовать причудливые Шаблоны Дизайна, чтобы описать это, такие как Фасад и Мементо. Но независимо от того, у вас есть множество атрибутов, которые нельзя легко запросить в SQL; Вы должны получить весь блоб обратно в приложение и отсортировать его там.
Entity-Attribute-Value : одна таблица для продуктов и одна таблица, которая сводит атрибуты к строкам, а не к столбцам. EAV не является допустимым проектом в отношении реляционной парадигмы, но многие люди все равно его используют. Это «Шаблон свойств», упомянутый в другом ответе. См. Другие вопросы с тегом eav в StackOverflow для некоторых ловушек.
Я написал об этом больше в презентации, Расширяемое моделирование данных .
Дополнительные мысли о EAV: Хотя многие люди предпочитают EAV, я не согласен. Похоже, самое гибкое решение и, следовательно, лучшее. Однако имейте в виду пословицу TANSTAAFL . Вот некоторые из недостатков EAV:
- Нет способа сделать столбец обязательным (эквивалент
NOT NULL
).
- Нет способа использовать типы данных SQL для проверки записей.
- Нет способа гарантировать, что имена атрибутов пишутся последовательно.
- Нет способа поместить внешний ключ в значения любого данного атрибута, например для справочной таблицы.
- Получение результатов в обычном табличном макете является сложным и дорогостоящим, потому что для получения атрибутов из нескольких строк вам нужно сделать
JOIN
для каждого атрибута.
Степень гибкости, которую дает вам EAV, требует жертв в других областях, что, вероятно, делает ваш код более сложным (или хуже), чем это было бы для решения исходной проблемы более традиционным способом.
И в большинстве случаев нет необходимости иметь такую степень гибкости. В вопросе OP о типах продуктов гораздо проще создать таблицу для каждого типа продукта для атрибутов, относящихся к конкретному продукту, поэтому у вас есть некоторая согласованная структура, применяемая по крайней мере для записей того же типа продукта.
Я бы использовал EAV, только если каждая строка должна иметь возможность иметь отдельный набор атрибутов. Когда у вас есть конечный набор типов продуктов, EAV является излишним. Наследование таблиц классов будет моим первым выбором.
Обновление 2019: чем больше я вижу людей, использующих JSON в качестве решения проблемы «много пользовательских атрибутов», тем меньше мне нравится это решение. Это делает запросы слишком сложными, даже если для их поддержки используются специальные функции JSON . Для хранения документов JSON требуется гораздо больше места, чем в обычных строках и столбцах.
По сути, ни одно из этих решений не является простым и эффективным в реляционной базе данных. Сама идея наличия «переменных атрибутов» в корне противоречит теории отношений.
То, что сводится к тому, что вы должны выбрать одно из решений, на основе которого наименее плохое для ваше приложение Поэтому вам нужно знать, как вы собираетесь запрашивать данные, прежде чем выбрать дизайн базы данных. Невозможно выбрать одно решение, которое является «лучшим», потому что любое из решений может быть лучшим для данного приложения.