Действительно ли плохо хранить список с разделителями в столбце базы данных? - PullRequest
337 голосов
/ 06 сентября 2010

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

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

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

Еще немного контекста, это небольшое внутреннее приложение, которое по существу заменяет файл Excel, который был сохраненОбщая папка.Я также спрашиваю, потому что я думаю о том, чтобы очистить программу и сделать ее более понятной.Там есть некоторые вещи, которыми я не совсем доволен, одна из них - тема этого вопроса.

Ответы [ 10 ]

530 голосов
/ 06 сентября 2010

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

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

Чтобы решить эти проблемы, вы должны написать тонны кода приложения, заново изобретая функциональные возможности, которые СУБД уже обеспечивает гораздо более эффективно .

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

Бывают случаи, когда вам нужно использовать денормализацию, но, как @ OMG Ponies упоминает , это исключительные случаи. Любая нереляционная «оптимизация» приносит пользу одному типу запроса за счет других видов использования данных, поэтому убедитесь, что вы знаете, какие из ваших запросов необходимо обрабатывать настолько специально, чтобы они заслуживали денормализации.


* MySQL 8.0 больше не поддерживает этот синтаксис выражения границы слова.

39 голосов
/ 06 сентября 2010

Существует множество вопросов, касающихся SO:

  • как получить число определенных значений из списка через запятую
  • как получить записи, которые имеют только те же самые 2 /3 / etc конкретное значение из этого списка, разделенного запятыми

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

38 голосов
/ 06 сентября 2010

«Одной из причин была лень».

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

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

(Некоторые пользователи будут оспариватьзаявление в моем предыдущем абзаце, в котором говорится, что «вы никогда не можете знать, какие требования будут добавлены в будущем». Эти пользователи либо вводят в заблуждение, либо высказывают религиозные убеждения. Иногда выгодно работать с требованиями, которые у вас были до вас.

18 голосов
/ 06 сентября 2010

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

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

12 голосов
/ 06 сентября 2010

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

Это нарушает первую нормальную форму.

Вторая критика заключается в том, что ввод необработанных исходных данных непосредственно в базу данных безпроверка или связывание вообще оставляют вас открытыми для атак с использованием SQL-инъекций.

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

Или оставить все как есть и выучить мучительный урок атаки SQL-инъекцией.

7 голосов
/ 12 июля 2013

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

7 голосов
/ 06 сентября 2010

Что ж, я использую разделенный табуляцией список ключ / значение в столбце NTEXT в SQL Server уже более 4 лет, и он работает. Вы теряете гибкость при создании запросов, но, с другой стороны, если у вас есть библиотека, которая сохраняет / удерживает пару ключей / значений, тогда это неплохая идея.

6 голосов
/ 06 сентября 2010

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

0 голосов
/ 01 декабря 2018

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

0 голосов
/ 06 сентября 2010

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

...