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

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

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

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

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

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

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

Ответы [ 4 ]

207 голосов
/ 30 марта 2009

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

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

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

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

13 голосов
/ 21 марта 2011

@ StoneHeart

Я бы пошел сюда с 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.

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

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

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

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

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

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

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

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

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

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

5 голосов
/ 16 сентября 2010

Если я использую 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 для взаимодействия с базой данных, чтобы получить информацию о продукте; затем, если какие-либо изменения в коде теперь требуют рефакторинга, они должны относиться только к этому классу.

3 голосов
/ 30 марта 2009

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

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

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

...