Невже зберігання обмеженого списку в стовпці бази даних насправді погано?


363

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

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

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

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


21
в такому випадку, чому турбувати базу даних ?, збережеться у файлі.
таван

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

1
У Postgres стовпчик масиву слід віддавати перевагу списку, розділеному комами. Що принаймні забезпечує належний тип даних, не має проблем з відмежуванням роздільника від реальних даних і його можна ефективно індексувати.
a_horse_with_no_name

Відповіді:


567

Окрім порушення першої нормальної форми через повторювану групу значень, що зберігаються в одному стовпчику, списки, розділені комами, мають ще багато інших практичних проблем:

  • Неможливо переконатися, що кожне значення є правильним типом даних: жоден спосіб запобігти 1,2,3, банан, 5
  • Неможливо використовувати обмеження іноземних ключів для прив'язки значень до таблиці пошуку; ніякого способу закріпити референтну цілісність.
  • Неможливо застосувати унікальність: жодний спосіб запобігти 1,2,3,3,3,5
  • Неможливо видалити значення зі списку, не отримуючи весь список.
  • Неможливо зберегти список довший, ніж той, що входить у стовпчик рядка.
  • Важко шукати в списку всі об'єкти із заданим значенням; вам доведеться використовувати неефективне сканування таблиці. Можливо, доведеться вдаватися до регулярних виразів, наприклад, в MySQL:
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • Важко порахувати елементи у списку або виконувати інші сукупні запити.
  • Важко приєднати значення до таблиці пошуку, на яку вони посилаються.
  • Складно отримати список у відсортованому порядку.

Щоб вирішити ці проблеми, вам доведеться записати тонни коду програми, відновлюючи функціональність, яку RDBMS вже забезпечує набагато ефективніше .

Списки, розділені комами, є досить неправильними, що я зробив це першим розділом у своїй книзі: SQL Antipatterns: Уникнення підводних каменів програмування баз даних .

Бувають випадки, коли вам потрібно застосовувати денормалізацію, але, як згадує @OMG Ponies , це випадки виключення. Будь-яка нереляційна «оптимізація» приносить користь одному типу запитів за рахунок іншого використання даних, тому не забудьте знати, які саме із ваших запитів потрібно обробляти так спеціально, що вони заслуговують денормалізації.


* MySQL 8.0 більше не підтримує цей синтаксис вираження прикордонного слова.


8
ARRAY (будь-якого типу даних) може виправити виняток, просто перевірте PostgreSQL: postgresql.org/docs/current/static/arrays.html (@Bill: Відмінна книга, обов'язково прочитати для будь-якого розробника або dba)
Frank Heikens

4
+1 законопроект Karwin Чудова відповідь! Прекрасні лаконічні точки кулі. Це теж схоже на чудову книгу. Любіть обкладинку +1 NullUserException. Я зараз розробляю схему для бази даних MySQL для заміни системи на основі текстового файлу з плоским файлом. Досі я стикався з кількома дилемами. Тож цю книгу варто буде придбати.
therobyouknow

2
Сайт pragprog.com також добре виглядає: приємний стиль, макет, зручність у користуванні. Це повинно бути досить новим, я не міг купувати їхні електронні книги раніше. PS. Я не працюю, щоб вони не мали жодного зв'язку з авторами. Мені подобається відзначати хороші товари, послуги та допомогу, коли я це бачу.
therobyouknow

2
Щодо серйозної сторони, я додам до вашого списку: Важкий пошук. Скажіть, що ви хочете, щоб усі записи, що включають "2". Звичайно, ви не можете просто шукати foobar = '2', тому що це було б пропущено, якби були інші значення. Ви не можете шукати foobar на зразок "% 2%", оскільки це отримало б помилкові звернення для 12 та 28 тощо. Ви не можете шукати фобар як "%, 2,%", тому що 2 може бути першим або останнім елементом списку, і тому є лише одна з цих коми.
Джей

2
Я знаю, що це не рекомендується, але гравці чортів захищають: більшість з них можна зняти, якщо є інтерфейс, який обробляє унікальність і типи даних (інакше буде помилка чи неправильно поводитись), користувальницький інтерфейс скидає і створює його все одно, є таблиця драйверів, де значення походять, щоб зробити їх унікальними, можна використовувати поле типу "% P%", значення P, R, S, T, підрахунок не має значення, а сортування не має значення. Залежно від ui, значення можуть бути розділені [], наприклад, щоб встановити прапорці в списку з таблиці драйверів, як мінімум, за звичайним сценарієм, без необхідності переходити до іншої таблиці, щоб отримати їх.
jmcclure

44

"Однією з причин була лінь".

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

Сказавши це: якщо дані, які ви вирішили зберігати таким чином, це дані, до яких вам ніколи не потрібно буде запитувати, то може виникнути випадок їх збереження у вибраному вами способі.

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


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

41

Є багато питань, які задають питання:

  • як отримати кількість конкретних значень зі списку, розділеного комами
  • як отримати записи, які мають лише те саме значення 2/3 / etc із цього списку, розділеного комами

Ще одна проблема зі списком, розділеним комами, полягає у забезпеченні послідовності значень - зберігання тексту означає можливість помилок друку ...

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


19

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

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


10

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

Це порушує першу нормальну форму.

Друга критика полягає в тому, що введення необроблених результатів введення безпосередньо в базу даних, без будь-якої перевірки чи прив'язки, залишає вас відкритими для атак ін'єкцій SQL.

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

Або залиште його таким, яким він є, і вивчіть болісний урок нападу ін'єкції SQL.


19
Я не бачу нічого в цьому питанні, яке б підказувало, що він вразливий до ін'єкцій SQL. Інжекція SQL та нормалізація бази даних є ортогональними темами, і ваше відступ від ін'єкції не має значення для питання.
Hammerite

5
@Paul: І, можливо, те саме ставлення призведе до того, що його вдарить автобус, коли він не перегляне обидва шляхи, перш ніж перейти вулицю, але ти про це не попередив його. Редагувати: Я думав, що ви афіша цієї відповіді, моя помилка.
Hammerite

1
@Hammerite - ваша екстраполяція на автобуси смішна.
duffymo

4
Так, це мало бути смішним. Його смішність ілюструє те, що я дотримуюся, - це те, що немає сенсу попереджати його проти чогось, про що у вас немає підстав вважати, що про нього потрібно попередити.
Hammerite

1
Так, я бачу. Я думаю, що у мене було набагато більше причин для вашого попередження про автобуси.
duffymo

7

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


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

5
Пол, я згоден. Але, як я вже сказав, я використовував це для певної мети, а це для операції введення даних, де у вас є багато видів форм. Я переглядаю дизайн тепер, коли я навчився NHibernate, але тоді мені знадобився flexibity, щоб розробити форму в ASP.NET і використовувати ідентифікатори текстового поля в якості ключа в парі ключ / значення.
Радж

28
+1 просто для протидії голосному моменту. Сказати комусь, хто протягом 4 років підтримує додаток про проблеми з технічним обслуговуванням, є трохи нахабним. У розробці SW дуже мало "жахливих" ідей - переважно це лише ідеї з дуже обмеженою застосованістю. Доцільно попередити людей про обмеження, але карати тих, хто це зробив і пережив це, вражає мене, як душевне ставлення, ніж я можу обійтися.
Марк Брекетт

7

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

Він може бути перетворений на коми, розмежовані за потребою

запит списку XML на сервері sql за допомогою Xquery .

Будучи полем xml, можна вирішити деякі проблеми.

За допомогою CSV: Неможливо переконатися, що кожне значення є правильним типом даних: жоден спосіб запобігти 1,2,3, банан, 5

З XML: значення в тегу можуть бути змушені бути правильним типом


З CSV: не можна використовувати обмеження для зовнішніх ключів для прив'язки значень до таблиці пошуку; ніякого способу закріпити референтну цілісність.

З XML: проблема залишається


З CSV: Неможливо застосувати унікальність: жодним чином не запобігти 1,2,3,3,3,5

З XML: проблема залишається


З CSV: неможливо видалити значення зі списку, не виймаючи весь список.

За допомогою XML: окремі елементи можна видалити


З CSV: важко шукати в списку всі об'єкти із заданим значенням; вам доведеться використовувати неефективне сканування таблиці.

За допомогою XML: поле xml можна індексувати


З CSV: важко порахувати елементи у списку або виконувати інші сукупні запити. **

З XML: не особливо важко


З CSV: важко приєднати значення до таблиці пошуку, на яку вони посилаються. **

З XML: не особливо важко


З CSV: важко отримати список у відсортованому порядку.

З XML: не особливо важко


За допомогою CSV: Збереження цілих чисел як рядків займає приблизно вдвічі більше місця, ніж зберігання двійкових цілих чисел.

З XML: зберігання навіть гірше, ніж у csv


З CSV: Плюс багато символів комами.

З XML: теги використовуються замість коми


Коротше кажучи, за допомогою XML можна вирішити деякі проблеми з обмеженим списком AND, при необхідності можна перетворити на розмежений список


6

Так, це так погано. На мою думку, якщо вам не подобається використовувати реляційні бази даних, тоді шукайте альтернативу, яка вам більше підходить, там є багато цікавих проектів "NOSQL" з деякими дійсно розширеними функціями.


0

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


Форма містить ще кілька полів, це лише одна частина форми (яку я не пояснив добре у питанні).
Mad Scientist

0

Якщо у вас є фіксована кількість булевих полів, ви можете використовувати INT(1) NOT NULL(або BIT NOT NULLякщо вони є) або CHAR (0)(нульові) для кожного. Ви також можете використати SET(я забуваю точний синтаксис).


1
INT(1)займає 4 байти; (1)НЕ має сенсу.
Рік Джеймс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.