Возможные преимущества хранения нескольких значений в одном поле одной строки вместо отдельных строк

11

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

"Будет ли сценарий, который оправдывает хранение данных в виде строки (строки) вместо нескольких строк?"

Давайте предположим таблицу с именем, countryStatesгде мы хотим хранить состояния страны; Я буду использовать США для этого примера и не буду перечислять все штаты ради лени.

Там у нас будет две колонки; один звонил, Countryа другой звонил States. Как обсуждалось здесь , и предложенный @ srutzky в ответ , то PKбудет код определяется стандартом ISO 3166-1 альфа-3 .

Наша таблица будет выглядеть так:

+---------+-----------------------+-------------------------------------------------------+
| Country | States                | StateName                                             |
+---------+-----------------------+-------------------------------------------------------+
| USA     | AL, CA, FL,OH, NY, WY | Alabama, California, Florida, Ohio, New York, Wyoming |
+---------+-----------------------+-------------------------------------------------------+

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

Мы пришли к выводу, что эта модель не очень полезна, но у меня возникло подозрение, что может быть способ сделать это полезным.

Я хотел бы спросить, видел ли кто-нибудь из вас, слышал или сделал что-то подобное таким образом, который действительно работает .

Человек, в конце концов
источник
Теперь представьте, что у вас есть вторая таблица «продажи», в которой есть данные о каждой продаже, которая произошла вместе с кодом штата, в котором произошла продажа. Как бы вы написали запрос, который генерирует отчет со столбцами (StateName, TotalSalesAmount)? Сложно, верно?
zgguy
В точку. Я тоже не согласен с этой моделью. Мы застряли в любой момент, когда нам нужно восстановить любой тип данных (или полезные данные, если хотите).
Human_AfterAll
Возможным сценарием может быть хранение переменных. Магазин a;b;c, использовать передний конец для разбора вашей строки вы получите то a, b, cи нести на выполнение делать что - то с ними, может быть ?. Чувствую, что это может удовлетворить какую-то конкретную потребность таким образом ... Если подумать, нет. Вы всегда можете хранить идентификаторы, присоединяться к своим таблицам и создавать объединенную строку, которая может отправлять содержимое в FE ...
Nelz
Чтобы быть справедливым (по крайней мере для меня ;-), я предложил использовать двухзначные коды стран :-) в этом другом ответе .
Соломон Руцкий
2
Обратите внимание, что никто не сомневается в том, чтобы хранить значение «Алабама» в столбце, вместо того, чтобы иметь отдельную таблицу со столбцами STATE, N & C для слова «имя состояния STATE имеет N-й символ C». Потому что либо 1. мы не собираемся запрашивать символы имен или 2. мы не против вызвать функцию NTH_CHAR (N, S), возвращающую «N-й символ строки S» в каждой строке с именем, если мы это сделаем , (Против JOIN и других реляционных операторов, исключающих некоторые такие строки через дополнительную таблицу.) То же самое для целых чисел и NTH_DIGIT (N, I). Это всегда призыв к суждению относительно того, что в конкретной базе данных является атомарно-реляционным.
philipxy

Ответы:

13

Начнем с того, что заголовок текущего вопроса, относящийся к «хранению данных в виде строки вместо столбцов», немного сбивает с толку. Говоря о хранении данных в виде строк вместо чего-то другого, это обычно относится к сериализации всего в строковый формат вместо правильного / сильного типа данных (например, INTили DATETIME). Но если спросить о сохранении данных в виде нескольких значений в одном поле, а не в отдельных строках, это немного отличается. И чтобы быть справедливым, хотя объединение значений проще всего сделать со строками, это также можно сделать с помощью INTи BINARYтипов, либо с помощью маскировки битов, либо аналогичным образом резервируя определенные позиции, чтобы иметь разные значения. Поскольку второе толкование - это то, о чем фактически спрашивают, основываясь на тексте Вопроса, давайте обратимся к этому.

Одним словом: Нет. Если вы храните фактические точки данных, то это принесет только боль (с точки зрения кода и производительности), поскольку это является ненужным осложнением. Если это значение, которое когда-либо будет храниться только как единое целое, обновляться как единое целое и никогда не разбираться в базе данных, тогда это может быть нормально, поскольку это примерно аналогично хранению изображения или PDF. В противном случае любая попытка проанализировать данные будет недействительной с использованием любых индексов (например, с использованием LIKE '%something%', или CHARINDEX, или PATINDEX, или SUBSTRING, и т. Д.).

Если вам нужно хранить отдельные значения в одном поле одной строки, то для этого есть более подходящие средства: XML или JSON. Это анализируемые форматы ( XML / JSON ), и XML можно даже индексировать . Но в идеале эти данные должны храниться в правильно заполненных полях, чтобы они могли быть действительно полезными.

И, пожалуйста, не забывайте, что целью СУБД является хранение данных таким образом, чтобы их можно было извлекать и манипулировать ими как можно более эффективно в рамках ограничений, налагаемых совместимостью с ACID . Получение сцепленных значений достаточно плохо из-за необходимости сначала анализировать значения, и это не индексируется. Но манипулирование часто означает замену всего большого двоичного объекта просто для обновления его части (при условии, что не существует шаблона для использования с REPLACEфункцией). Тип данных XML, по крайней мере, позволяет использовать XML DML для упрощенных обновлений, хотя они все еще не так быстры, как простое обновление правильно смоделированных данных.

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

А что, если бизнес-требования со временем меняются, и вам необходимо отслеживать дополнительные свойства этих элементов? С точки зрения «штатов», как насчет столиц, или населения, или порядка сортировки, или чего-то еще? Правильно хранятся в виде строк, вы можете добавить больше столбцов для дополнительных свойств. Конечно, у вас может быть несколько уровней данных, которые можно анализировать, например, |StateCode,Capital,Population |StateCode,Capital,Populate|...но, надеюсь, любой может увидеть, что проблема экспоненциально выходит из-под контроля. Конечно, эта конкретная проблема довольно легко решается с форматами XML и JSON, и это их значение, как указано выше. Но вам все равно понадобится очень веская причина для использования любого из них в качестве начального средства моделирования, поскольку ни один из них не будет столь же эффективным, как использование дискретных полей в отдельных строках.

Соломон Руцкий
источник
9

Я действительно использовал что-то подобное для очень ограниченной цели. Мы создали таблицу заголовков для выходных файлов. Они были специально построены и были в основном только заголовками столбцов, но не совсем. Таким образом, данные выглядели примерно так

OutputType   OutputHeader
PersonalData Name|Address|City|State|Zip
JobInfo      Name|JobName|JobTitle

По сути это выглядело как список с разделителями. И в одном смысле это было. Но для наших целей это была единственная длинная строка.

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

Кеннет Фишер
источник
1

Я использовал это однажды с довольно маленькой таблицей, например:

CREATE TABLE t1 (
  ID number,
  some_feature   varchar2(100),
  valid_channels  varchar2(100));

CREATE TABLE channel_def (
  channel varchar2(100));

А затем сохранить значения CRM,SMS,SELF-CAREв valid_channel.

Вся таблица содержит около 10 записей. valid_channelсодержит значения, которые на самом деле должны быть в таблице связей, которая отображает отношение «многие ко многим». Стол t1не будет интенсивно использоваться, поэтому мы просто решили пойти по этому пути. Тем не менее, некоторые политики были вовлечены в это решение (см. Ниже).

Но в целом я этого избегаю, это не 3NF.

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

SELECT * 
  FROM t1 
 INNER JOIN channel_def cd
    ON ','||t1.valid_channels||',' LIKE '%,'||cd.channel||',%';

Ужасно + на Oracle это отключает использование индекса из-за запуска '%,'.

Robotron
источник
Что будет медленнее: LIKEили простое соединение?
Human_AfterAll
Лучше иметь объединение для столбца, который проиндексирован или по крайней мере имеет ссылочное ограничение (FK). Кроме того, объединения обычно выполняются на ПК другой таблицы, которая индексируется по умолчанию (по крайней мере, в Oracle). Если вы спрашиваете о конкретном случае под рукой (см. Выше), план выполнения, скорее всего, скажет, что он такой же, так как это была маленькая таблица.
Роботрон
@Human_AfterAll LIKEбудет медленнее, особенно если данные правильно смоделированы для использования TINYINTполя PK в channel_def. Тогда нужно только сравнить один байт между двумя таблицами. Здесь он должен анализировать строку, символ за символом (по крайней мере, до тех пор, пока условие не будет выполнено), и выполняет поиск без учета регистра (на основе заданной таблицы, не показывающей используемое _BIN2сопоставление). Это также делает недействительными индексы на SQL Server. Я ответил на это в своем ответе, сказав, что при разборе нельзя использовать индексы. Я просто обновил свой ответ, чтобы сделать его более понятным.
Соломон Руцкий
1
@Human_AfterAll Я бы сказал, что это модельное решение было принято из-за недостатка опыта и знаний (а иногда и лени). Один дополнительный JOIN - это все, что сохраняется, но жертвуется способность внешнего ключа, которая предотвращает попадание полностью фиктивных данных (даже если это не соответствует LIKEпредложению и не дает странных результатов, это может вызвать другие проблемы или по крайней мере, сделать отладку сложнее / дольше). Это также усложняет обновление valid_channelsполя. Это не значит, что это не работает, просто нет веских причин для этого.
Соломон Руцкий
«недостаток опыта» - хуже всего то, что это конкретное проектное решение было навязано старшим сотрудником ...
Роботрон
1

Это было сделано здесь, на SE. Как пишет Марк Гравелл :

... После некоторых размышлений и размышлений мы остановились на естественном представлении с разделителями в виде строки (бара) с ведущими / завершающими каналами, поэтому «.net c #» становится просто «| .net | c # |». Это имеет достоинства:

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

Этот «новый формат» был следующим шагом по сравнению со «старым форматом», который немного отличался и был выбран для использования функции полнотекстового поиска SQL Server, поэтому некоторые преимущества не имеют значения, если вы делаете это с нуля.

Предположительно, они не полностью нормализовали вещь по причинам объема работы и производительности.

Евгений Рябцев
источник
0

Ну, одно из основных преимуществ использования строк и других типов данных - отправка их из SQL Server в C #, C, C ++ (и т. Д.) С использованием SQLCLR, когда может потребоваться высокая производительность. Вы могли бы даже создать представление или хранимую процедуру для представления реляционных данных нереляционно - как вы сделали для примера выше для этой цели.

Смотрите этот пример:

http://aboutsqlserver.com/2013/07/22/clr-vs-t-sql-performance-considerations/

per Wikipedia: SQL CLR или SQLCLR (SQL Common Language Runtime) - это технология для размещения общеязыкового движка Microsoft .NET в SQL Server. SQLCLR позволяет размещать управляемый код и выполнять его в среде Microsoft SQL Server.

ужалить
источник
2
Всем привет. Можете ли вы дать более подробную информацию здесь. Я не уверен, как это выгодно хранить данные нетрадиционными способами. Во всяком случае, SQLCLR выгодно иметь возможность лучше справляться с альтернативными форматами данных, если они должны существовать. Но это не причина предпочитать альтернативный формат данных. Таким образом, я действительно не думаю, что это отвечает на вопрос.
Соломон Руцкий
Ссылка на статью объясняет преимущества за и против. Кроме того, я упомянул о хранении данных реляционно, и в целях CLR преобразование их в нереляционные с помощью представления или хранимой процедуры. Ваш вопрос был "Будет ли сценарий, который оправдывает хранение данных в виде строки (строки) вместо нескольких строк?" И мой ответ был положительным, хотя я предпочитаю представление или хранимую процедуру для целей взаимодействия с CLR.
Стинг
0

На мой взгляд, ответ будет отрицательным. Я не использовал этот подход и избежал бы его - я не могу придумать причину, по которой я пошел бы по этому пути. Вы склоняетесь к миру JSON / NoSQL с массивом.

У нас были аналогичные варианты дизайна в предыдущей роли, когда команда архитекторов хотела иметь поле «Данные», которое было разделено, а затем преобразовано в двоичный файл. Мы не пошли по этому маршруту в конце концов по нескольким причинам.

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

Клайв Стронг
источник