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