Як створити таблицю продуктів для багатьох видів продукції, де кожен продукт має багато параметрів


140

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

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

    • Телефон матиме колір, розмір, вагу, ОС ...

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

  • Набір параметрів повинен бути динамічним. Ви можете додати або відредагувати будь-який параметр, який вам подобається.

Як я можу виконати ці вимоги без окремої таблиці для кожного виду товару?

Відповіді:


233

У вас є принаймні ці п'ять варіантів моделювання ієрархії типів, яку ви описуєте:

  • Спадництво однієї таблиці: одна таблиця для всіх типів продукту з достатньою кількістю стовпців для зберігання всіх атрибутів усіх типів. Це означає багато стовпців, більшість з яких NULL у будь-якому заданому рядку.

  • Наслідування таблиці класів : одна таблиця для продуктів, що зберігає атрибути, спільні для всіх типів продуктів. Потім одна таблиця на тип продукту, зберігаючи атрибути, характерні для цього типу продукту.

  • Успадкування конкретної таблиці : немає таблиці для загальних атрибутів Продуктів. Натомість одна таблиця на тип продукту, що зберігає як загальні атрибути продукту, так і специфічні для продукту атрибути.

  • Серіалізований LOB : одна таблиця для продуктів, що зберігає атрибути, спільні для всіх типів продуктів. В одному додатковому стовпці зберігається BLOB напівструктурованих даних у форматі XML, YAML, JSON чи іншому форматі. Цей BLOB дозволяє зберігати атрибути, характерні для кожного типу продукту. Для опису цього ви можете скористатися фантазійними шаблонами дизайну, такими як "Фасад" та "Мементо". Але незалежно від того, у вас є атрибути, які не можна легко запитати в SQL; ви повинні повернути всю крапку до програми та розібрати її там.

  • Entity-Attribute-Value : одна таблиця для продуктів та одна таблиця, яка перетворює атрибути до рядків, а не стовпців. EAV не є дійсною конструкцією щодо реляційної парадигми, але багато людей її використовують у будь-якому випадку. Це "Шаблон властивостей", згаданий іншою відповіддю. Дивіться інші питання з тегом eav на StackOverflow для деяких підводних каменів.

Про це я написав більше у презентації « Розширене моделювання даних» .


Додаткові думки про EAV: Хоча багато людей, здається, віддають перевагу EAV, я не хочу. Це здається найбільш гнучким рішенням, а отже, найкращим. Однак майте на увазі приказку TANSTAAFL . Ось деякі недоліки EAV:

  • Жоден спосіб зробити колонку обов'язковою (еквівалент NOT NULL).
  • Немає можливості використовувати типи даних SQL для перевірки записів.
  • Жоден спосіб забезпечити послідовність написання імен атрибутів.
  • Ні в якому разі не можна ставити іноземний ключ на значення будь-якого заданого атрибуту, наприклад, для таблиці пошуку.
  • Отримання результатів у звичайному табличному макеті є складним і дорогим, оскільки для отримання атрибутів з декількох рядків вам потрібно зробити JOINдля кожного атрибута.

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

І в більшості випадків зайвим є такий ступінь гнучкості. У питанні ОП про типи продуктів набагато простіше створити таблицю за типом продукту для атрибутів, що стосуються конкретного продукту, так що у вас є певна послідовна структура, принаймні для записів одного типу продукту.

Я б використовував EAV лише в тому випадку, якщо в кожному рядку повинно бути дозволено мати різний набір атрибутів. Якщо у вас є обмежений набір типів продуктів, EAV є надмірним. Спадщина таблиці класів - це мій перший вибір.


Оновлення 2019 року: чим більше я бачу людей, які використовують JSON як рішення для проблеми "безліч спеціальних атрибутів", тим менше мені подобається це рішення. Це робить запити занадто складними, навіть коли використовуються спеціальні функції JSON для їх підтримки. Для зберігання документів JSON потрібно набагато більше місця для зберігання в звичайних рядках і стовпцях.

В основному, жодне з цих рішень не є легким або ефективним у реляційній базі даних. Вся ідея наявності "змінних атрибутів" принципово суперечить реляційній теорії.

Це зводиться до того, що вам потрібно вибрати одне з рішень, на основі якого є найменш поганим для вашої програми. Тому вам потрібно знати, як ви збираєтеся запитувати дані, перш ніж вибрати дизайн бази даних. Немає можливості вибрати одне рішення, яке є "найкращим", оскільки будь-яке рішення може бути найкращим для даної програми.


11
@HimalayaGarg Варіант "4.5" дійсно є протилежною всій точці посту Білла.
user3308043

2
На відміну від MySQL, SQL Server має широку підтримку XML, XPath та XQuery. Тож для користувачів SQL Server найкращим варіантом було б зберігання додаткових атрибутів у стовпці типу XML (варіант 4). Таким чином, вам НЕ доведеться "повернути всю крапку до програми та розібрати її там". Ви навіть можете створювати індекси на стовпцях XML у SQL Server.
Delphi.Boy


2
Я віддаю перевагу серіалізованому LOB для свого випадку. Але чи підходить він для ORM? Я використовую EF.
Махмуд Дженамі

@ user2741577, звичайно, але вам, мабуть, доведеться написати спеціальний код, щоб розпакувати поля неструктурованих даних з LOB і застосувати їх до кожного поля об'єкта вашого ORM-об’єкта. Я не знаю EF, але, мабуть, ви могли б створити базовий клас ORM, який це робить. Потрібно відслідковувати, які поля надходили з конкретних полів рядка бази даних, а які поля надходили з полів LOB, щоб ви могли сформувати LOB, коли настане час зберегти об’єкт.
Білл Карвін

12

@StoneHeart

Я б поїхав сюди з EAV та MVC всю дорогу.

@Bill Karvin

Ось деякі недоліки EAV:

  • Ні в якому разі не можна зробити стовпчик обов'язковим (еквівалент НЕ NULL).
  • Немає можливості використовувати типи даних SQL для перевірки записів.
  • Жоден спосіб забезпечити послідовність написання імен атрибутів.
  • Ні в якому разі не можна ставити іноземний ключ на значення будь-якого заданого атрибуту, наприклад, для таблиці пошуку.

Усі ті речі, про які ви згадали тут:

  • перевірка даних
  • атрибут імен перевірки правопису
  • обов'язкові стовпці / поля
  • обробка знищення залежних атрибутів

на мою думку, взагалі не належать до бази даних, оскільки жодна з баз даних не може обробляти ці взаємодії та вимоги на належному рівні, як це робить мова програмування програми.

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

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

Цю проблему можна вирішити, зробивши кілька запитів щодо часткових даних та обробивши їх у табличному макеті за допомогою програми. Навіть якщо у вас є 600 ГБ даних про продукт, ви можете обробляти їх партіями, якщо вам потрібні дані з кожного ряду цієї таблиці.

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

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

Якщо ви все ще переймаєтесь виконанням операцій, які виконуються додатком, ви завжди можете використовувати Erlang, C ++, Go Language для попередньої обробки даних, а згодом просто обробляти оптимізовані дані далі у вашому головному додатку.


you can always use Erlang, C++, Go Language to pre-process the dataЩо ти маєш на увазі? Замість БД використовуйте Go lang? Чи можете ви детальніше розібратися в цьому?
Зелений

1
Я цілком погоджуюся. EAV - це шлях, особливо якщо вам потрібен рівень гнучкості, який би дозволив вам додати новий вид продуктів та параметрів без змін схеми db, я маю на увазі живу у виробництві за допомогою програми. Був там зробив те. Працювали для мене. Про повільні запити ... хтось тут ніколи не чув про кеші? ;)
pawel.kalisz

@Green Я відредагував останній абзац, щоб зробити його більш зрозумілим, але мова йде про передачу ваших необроблених даних EAV в процес мовою, яка може вирішувати перетворення даних, пошук у структурі дерева або будь-яку основну карту, зменшуючи операції дуже швидко і ефективним способом пам'яті. Специфіка тут залежатиме від того, що потрібно оптимізувати
Pawel Barcik

6

Якщо я використовую 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

Ви можете мати таблицю продуктів і окрему таблицю ProductAdditionInfo з 3 стовпцями: ідентифікатор продукту, назва додаткової інформації, додаткова інформація. Якщо кольори використовуються багатьма продуктами, але не всіма видами продуктів, то це може бути нульовим стовпцем у таблиці продуктів або просто помістити його в ProductAdditionalInfo.

Цей підхід не є традиційною методикою для реляційних баз даних, але я бачив, що він багато використовувався на практиці. Він може бути гнучким і мати хороші показники.

Стів Єгге називає цей шаблон властивостей і написав довгий пост про його використання.


4
Шаблон властивостей - це лише значення Entity-Attribute-Value іншим іменем. Він використовується широко, але зберігання його у реляційній базі даних порушує правила нормалізації.
Білл Карвін

2
Якщо чесно, коли я прочитав опис EAV у відповіді @Bills, я не зовсім зрозумів, що він пояснює. Але коли ти сказав, 3 columns: product ID, additional info name, additional info valueя зрозумів цю концепцію. І я фактично робив це раніше, і зіткнувся з проблемами. Однак я не пригадую на даний момент, які були ці проблеми.
JD Isaacks

1
@JDIsaacks У цій схемі поширеною проблемою є те, що ми не знаємо, скільки ПРИЄДНАНЬ нам потрібно, щоб отримати всі атрибути.
Омід
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.