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

StackOverflow https://stackoverflow.com/questions/695752

Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

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

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

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

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

  • Сериализованный LOB:Одна таблица для продуктов, в которой хранятся атрибуты, общие для всех типов продуктов.В одном дополнительном столбце хранится BLOB-объект полуструктурированных данных в формате XML, YAML, JSON или каком-либо другом формате.Этот BLOB-объект позволяет хранить атрибуты, специфичные для каждого типа продукта.Для описания этого можно использовать причудливые шаблоны проектирования, такие как Facade и Memento.Но несмотря на то, что у вас есть набор атрибутов, которые нелегко запросить с помощью SQL;вам нужно вернуть весь объект обратно в приложение и там разобраться.

  • Сущность-атрибут-значение:Одна таблица для продуктов и одна таблица, в которой атрибуты сводятся к строкам, а не к столбцам.EAV не является допустимой конструкцией с точки зрения реляционной парадигмы, но многие люди все равно ее используют.Это «Шаблон свойств», упомянутый в другом ответе.Посмотрите другие вопросы с eav-тег на StackOverflow, чтобы узнать о некоторых подводных камнях.

Подробнее об этом я писал в презентации. Расширяемое моделирование данных.


Дополнительные мысли о EAV:Хотя многие люди предпочитают EAV, я нет.Кажется, это самое гибкое решение и, следовательно, лучшее.Однако помните пословицу ТАНСТААФЛ.Вот некоторые недостатки EAV:

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

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

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

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


Обновление 2019 года:Чем больше я вижу людей, использующих JSON в качестве решения проблемы «множества пользовательских атрибутов», тем меньше мне нравится это решение.Это делает запросы слишком сложными, даже при использовании специальных JSON-функции чтобы поддержать их.Для хранения документов JSON требуется гораздо больше места, чем для хранения в обычных строках и столбцах.

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

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

Другие советы

@Каменное сердце

Я бы полностью остановился на EAV и MVC.

@Билл Карвин

Вот некоторые из недостатков 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.

для таблицы поиска.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top