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

У меня нет большого опыта в дизайне таблиц. Моя цель - создать одну или несколько таблиц продуктов, которые отвечают следующим требованиям:

  • Поддержка многих видов продуктов (телевизор, телефон, ПК,...). Каждый вид продукта имеет другой набор параметров, например:

    • Телефон будет иметь цвет, размер, вес, ОС...

    • У ПК будет процессор, жесткий диск, оперативная память...

  • Набор параметров должен быть динамическим. Вы можете добавлять или редактировать любые параметры, которые вам нравятся.

Как я могу удовлетворить эти требования без отдельной таблицы для каждого вида продукта?

Ответ 1

У вас есть по крайней мере пять вариантов моделирования иерархии типов, которую вы описываете:

  • Наследование одной таблицы: одна таблица для всех типов Продукта, с достаточным количеством столбцов для хранения всех атрибутов всех типов. Это означает много столбцов, большинство из которых имеют значение NULL в любой строке.

  • Наследование таблиц классов: одна таблица для продуктов, хранящая атрибуты, общие для всех типов продуктов. Затем по одной таблице для каждого типа продукта, сохраняя атрибуты, специфичные для данного типа продукта.

  • Наследование бетонной таблицы: нет таблицы для общих атрибутов продуктов. Вместо этого одна таблица для каждого типа продукта, в которой хранятся как общие атрибуты продукта, так и атрибуты, специфичные для продукта.

  • Сериализированный большой объект: одна таблица для продуктов, в которой хранятся атрибуты, общие для всех типов продуктов. В одном дополнительном столбце хранится большой двоичный объект полуструктурированных данных в формате 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 требуется гораздо больше места, чем в обычных строках и столбцах.

По сути, ни одно из этих решений не является простым или эффективным в реляционной базе данных. Сама идея наличия "переменных атрибутов" в корне противоречит теории отношений.

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

Ответ 2

@StoneHeart

Я бы пошел сюда с EAV и MVC полностью.

@Bill Karvin

Вот некоторые из недостатков EAV:

No way to make a column mandatory (equivalent of NOT NULL).
No way to use SQL data types to validate entries.
No way to ensure that attribute names are spelled consistently.
No way to put a foreign key on the values of any given attribute, e.g.

для справочной таблицы.

Все те вещи, которые вы упомянули здесь:

  • проверка данных
  • Проверка имен имен атрибутов
  • обязательные столбцы/поля
  • обработка уничтожения зависимых атрибутов

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

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

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

Эту проблему можно решить, сделав несколько запросов на частичные данные и обработав их в виде таблиц с вашим приложением. Даже если у вас есть 600 ГБ данных продукта, вы можете обрабатывать его партиями, если вам нужны данные из каждой строки в этой таблице.

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

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

Если вы все равно будете заботиться о производительности операций, выполняемых приложением, вы всегда можете использовать Erlang, С++, Go Language для предварительной обработки данных, а затем просто обрабатывать оптимизированные данные в главном приложении.

Ответ 3

Если я использую Class Table Inheritance значение:

одна таблица для продуктов, сохраняющая атрибуты, общие для всех типов продуктов. Затем одна таблица для каждого типа продукта, сохраняющая атрибуты, специфичные для этого типа продукта. -Бил Карвин

Что мне нравится в лучших предложениях Билла Карвина. Я могу предвидеть один из недостатков, который я попытаюсь объяснить, как не стать проблемой.

Какой план непредвиденных обстоятельств должен иметь место, когда атрибут, который является общим только для 1 типа, затем становится общим для 2, затем 3 и т.д.?

Например: (это всего лишь пример, а не моя реальная проблема)

Если мы продаем мебель, мы можем продавать стулья, лампы, диваны, телевизоры и т.д. Тип телевизора может быть единственным типом, который мы носим с потреблением энергии. Поэтому я бы поместил атрибут power_consumption в tv_type_table. Но затем мы начинаем носить системы домашнего кинотеатра, которые также имеют свойство power_consumption. OK его только один другой продукт, поэтому я добавлю это поле в stereo_type_table, так как это, вероятно, проще всего на этом этапе. Но со временем, когда мы начнем носить все больше электроники, мы понимаем, что power_consumption достаточно широка, чтобы она была в main_product_table. Что мне теперь делать?

Добавьте поле в main_product_table. Напишите script, чтобы прокрутить электронику и установить правильное значение от каждого type_table до main_product_table. Затем отпустите этот столбец из каждого type_table.

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

Ответ 4

У вас может быть таблица Product и отдельная таблица ProductAdditionInfo с тремя столбцами: идентификатор продукта, дополнительное информационное имя, дополнительная информация. Если цвет используется многими, но не всеми видами Продуктов, вы можете иметь его столбцом с нулевым значением в таблице Product или просто поместить его в ProductAdditionalInfo.

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

Стив Йегг называет это шаблон свойств и написал длинный пост об использовании его.