Чи погана практика дозволити визначені користувачем поля?


17

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

Наприклад, я роблю домашню інвентаризацію для своєї дружини, і вона хоче визначити свої власні поля для різних предметів. Я планував дозволити їй створити категорії предметів і додати "функції" до цих категорій. Особливості просто ключ / значення, що зберігаються у вигляді рядків. Таким чином, якби у неї була, наприклад, категорія "Аудіо компакт-диски", вона могла б додавати функції для таких речей, як "художник", "треки" тощо. "(дерево, пластик тощо). Тоді будь-який предмет може належати до однієї (або багатьох) категорій, додаючи ці функції до елемента.

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

Взагалі кажучи, як люди обробляють щось подібне в "реальному житті"?


4
Чи розглядали ви використання бази даних, орієнтованої на документи, як MongoDB? Ви можете зберігати документ кожного типу, який виконує функції схеми, яку також можна редагувати (можливо, вручну, враховуючи невеликий масштаб проекту).
Енді Хант

@AndyBursh одним із "забавних" бітів із поточними постгресами є тип даних "json" ( посилання ). Такий підхід дозволив би зберігати вказані користувачем поля в цих даних, er, document, er, будь-що, а потім використовувати решту полів для речей, на які належним чином індексується тощо. Хоча все це залежить від використання та важко сказати, чи буде це добре для конкретного додатка чи ні. Але це щось, що слід пам’ятати.

всі: чудова дискусія, дякую за все прозріння! @AndyBursh Я чув про MongoDB, але ніколи не читав його.
Здається, що

Відповіді:


19

Коли ви починаєте переходити до "визначених користувачем полів", як це часто зустрічається в трекерах помилок, керуванні ресурсами клієнтів та подібних бізнес-інструментах, це те, що вони не підкріплені таблицею з мільйонними полями (якщо вони є, то ймовірно, це проблема своє).

Замість цього ви знайдете проекти таблиць значення атрибутів сутності та пов'язаний інструмент адміністрування для управління дійсними атрибутами.

Розглянемо наступну таблицю:

  + -------------- +
  | річ |
  | -------------- |
  | id |
  | тип |
  | desc |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + -------------- +

Це після того, як ви додали кілька атрибутів. Замість того , щоб attr1робити вигляд , що читає artistабо tracksабо genreабо будь-які атрибути речі є. І замість 5, що робити, якщо це було 50. Очевидно, що це не піддається управлінню. Також потрібно оновити модель та перерозподілити додаток для обробки нового поля. Не ідеально.

Тепер розглянемо таку структуру таблиці:

  + -------------- + + --------------- + + ------------- +
  | річ | | thing_attr | | attr |
  | -------------- | | --------------- | | ------------- |
  | id | <--- + | thing_id (fk) | +> | id |
  | тип | | attr_id (fk) | + - + | назва |
  | desc | | значення | | |
  + -------------- + + --------------- + + ------------- +

У вас є ваша річ з її основними полями. У вас є ще дві таблиці. Один з атрибутами. Кожне поле - це рядок у attrтаблиці. А потім є thing_attrсторона із закордонними клавішами, що відносяться до thingстолу та attrстолу. Тоді у цьому полі є значення, у якому ви зберігаєте незалежно від значення поля для цієї сутності.

А тепер у вас є структура, в якій таблицю attr можна оновлювати під час виконання, а нові поля можна додавати (або видаляти) на льоту, без істотного впливу на загальну програму.

Запити трохи складніші, і перевірка також стає складнішою (або фанкі, що зберігаються, або всі клієнтські сторони). Це торгівля дизайном.

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


Чи є недоліки просто змінити схему під час виконання? Якщо користувач вважає, що річ потребує нового атрибута, просто динамічно додайте стовпчик до таблиці?

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

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

  • Ви займаєтесь програмуванням метабаз. Замість того, щоб мати можливість чітко відобразити цей стовпець у цьому полі за допомогою хорошого ORM, ви, ймовірно, робите такі речі, як, select *а потім робите якийсь складний код, щоб дізнатися, що саме є даними (див . ResultSetMetaData Java ), а потім зберігаєте їх у карті ( або якийсь інший тип даних - але не непогані поля в коді). Це викидає неабияку безпеку типу та друку, яку ви маєте при традиційному підході.
  • Ви, ймовірно, відмовилися від ORM. Це означає, що ви пишете raw sql для всього коду, а не дозволяєте системі виконувати роботу за вас.
  • Ви кинули робити чисті оновлення. Що відбувається, коли клієнт додає поле з одним ім'ям, яке також використовує ваша наступна версія? На сайті сватання оновлення, яке хоче додати hasdateполе для зберігання часової позначки, вже визначене як hasdateз булевим для успішного матчу ... і ваше оновлення перерветься.
  • Ви довіряєте, що клієнт не порушує систему, використовуючи якесь зарезервоване слово, яке також порушує ваші запити ... десь.
  • Ви прив'язали себе до однієї марки БД. DDL різних баз даних відрізняються. Типи баз даних - найпростіший приклад цього. varchar2проти textтощо. Ваш код для додавання стовпця буде працювати на MySQL, але не на Postgres або Oracle або SQL Server.
  • Чи довіряєте ви клієнт насправді додати дані добре ? Звичайно, EAV далеко не ідеальний, але тепер у вас є жахливі незрозумілі назви таблиць, які розробник не додав, з неправильним типом індексу (якщо такий є), без обмежень, що додаються в код, де потрібно бути тощо.
  • Ви надали права модифікації схеми користувачеві, який запускає додаток. Таблиці скидання Little Bobby неможливі, якщо ви обмежені в SQL, а не DDL (впевнені, що можете зробити це delete * from studentsзамість цього, але ви не можете дійсно зіпсувати базу даних поганими способами). Кількість речей, які можуть зірватися з доступом до схеми або через аварію чи зловмисну ​​активність.

Це дійсно зводиться до "не робіть цього". Якщо ви дійсно цього хочете, перейдіть до відомого шаблону структури таблиці EAV або бази даних, яка повністю присвячена цій структурі. Не дозволяйте людям створювати довільні поля в таблиці. Головні болі просто не варті того.


4
Ви також винаходили базу даних.
користувач253751

1
@immibis він додав шар, в якому користувач може адмініструвати, не маніпулюючи рештою бази даних або вимагаючи перерозподілу для оновлення моделі.

1
@immibis EAV протягом багатьох років гаряче дискутував у реляційних колах баз даних. Теоретично це непотрібно, але на практиці ви не можете робити певні речі без цього.
Росс Паттерсон

1
@ShivanDragon, який переходить до підходу NoSQL. У магазині документів просто зберігаються документи і не накладається схема. Таким чином, додавання та вилучення полів та аналіз документів повністю виходять за межі самої бази даних (і ви написали свою модель для її урахування). Це зовсім інший набір компромісів, ніж реляційна база даних компромісів для структури EAV.


5

Зробити це добре, важко.

Для одноразового додатка, такого, як ви плануєте, ви, звичайно, можете просто додати стовпчик для кожного поля та надати користувальницький інтерфейс, який робить визначення поля нетренованими користувачами безпечнішим, ніж надання їм командного рядка SQL. Або ви можете слідувати жахливій схемі Entity-Attribute-Value , яка є класичною, якщо дещо страшною, відповіддю на подібну проблему. Побудова інтерфейсу користувача для визначення EAV-полів зазвичай набагато складніша, ніж для стовпців бази даних, і запити можуть бути досить волохатими, але для великої кількості полів ( тобто схеми з дуже рідкою матрицею) це може бути єдиним способом отримання виконана робота.


Підсумовуючи: маленький проект == KISS. Спритний до землі.
Encaitar

Проблема з оновленнями таблиці баз даних полягає в тому, що залежно від обсягу даних та необхідних індексів (користувацькі поля часто вимагають функцій пошуку), запит на зміну таблиці може зайняти мамонтову кількість часу. Коротка історія полягає в тому, що MySQL та інші реляційні бази даних просто не є хорошим середовищем для такого роду вимог.
Оддман

0

Нещодавно я прийшов на хрест.

Я зробив 2 таблиці.

1: table Objects 
    Id , name, type

Він усі ваші об’єкти. U встановити назву його.

І тип цього об’єкта: - для мене доступними типами були інвентар, інвентар_ітем, офіс.

І звичайним налаштуванням було n елементів - це дочірній або інвентар, який також є дочірнім офісом, і я використовував таблицю приєднання, щоб об'єднати об'єкти один з одним

2 table settings 
     organization_Id , title, value , type

Таблиця налаштувань містить кожну назву поля для конкретного типу об’єкта та значення у значенні.

Приклад властивостей офісу

Місцезнаходження, телефон, робочий час

І для предметів

  • Сума
  • Ціна
  • Штрих-код

І т.д., всі ці властивості застосовуються вашою моделлю та зберігаються в таблиці налаштувань як окремі рядки (проте використовуйте заміну не вставити, щоб уникнути декількох рядків для одного поля)

Тому коли-небудь я хочу офіс, я легко завантажую його всі відносини та налаштування, де налаштування object_I'd в (запитувані об'єкти)

Після цього я повертаю всі рядки з налаштувань, і все.

І якщо я хотів, щоб параметр був специфічним для елемента в інвентарі (не глобального), я встановлюю object_I'd = я з таблиці відносин object_objects і встановлюю settings.type = Relations_setting

Сподіваюся, ви зрозуміли, що я маю на увазі, я спробую переформатувати відповідь, коли потрапляю на ноутбук


2
Поради - не публікуйте на цьому форумі зі свого телефону. Автокоригування робить частини вашої публікації нечитабельними.
BobDalgleish

Ха-ха приємне спостереження :)
Залабоза

0

Чи погана практика дозволити визначені користувачем поля?

Ні, це не погана практика. Це досить поширене явище. У термінах ОО це називається спадкуванням. У вас є базовий клас invenItem та два успадкованих класу AudioCD та меблі.

Взагалі кажучи, як люди обробляють щось подібне в "реальному житті"?

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

Якщо простий запит є найважливішим для вас, а db-простір / нормалізація не має значення, ви б реалізували схему "таблиця за ієрархією".

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

Докладніше див. Таблиця dotnet table-per-type-vs-table-per-hierarchy-nasledstvo або спадщина java hibernate .


Я не знаю, чи це стосується цього питання. Користувач не змінює код для створення нових класів
Colin D
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.