Какие варианты использования для выбора CHAR над VARCHAR в SQL?

270

Я понимаю, что CHAR рекомендуется, если все мои значения фиксированной ширины. Но что с того? Почему бы просто не выбрать VARCHAR для всех текстовых полей, просто чтобы быть в безопасности.

SkunkSpinner
источник

Ответы:

386

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

Это зависит от реализации БД, но обычно VARCHAR использует еще один или два байта памяти (для длины или завершения) в дополнение к фактическим данным. Таким образом (при условии, что вы используете однобайтовый набор символов), сохраняя слово "FooBar"

  • CHAR (6) = 6 байтов (без накладных расходов)
  • VARCHAR (10) = 8 байтов (2 байта служебных данных)
  • CHAR (10) = 10 байтов (4 байта служебных данных)

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

Примечание : Microsoft SQL имеет 2 байта служебной информации для VARCHAR. Это может варьироваться от DB к DB, но обычно для указания длины или EOL на VARCHAR требуется как минимум 1 байт служебной информации.

Как было отмечено Gaven в комментариях, если вы используете многобайтовый набор символов переменной длины, такой как UTF8, то CHAR хранит максимальное количество байтов, необходимое для хранения количества символов. Таким образом, если UTF8 требуется максимум 3 байта для хранения символа, то для CHAR (6) будет установлено значение 18 байтов, даже если будет храниться только латинский 1 символ. Таким образом, в этом случае VARCHAR становится намного лучшим выбором.

Джим Маккит
источник
20
Другая причина - разделение страниц и фрагментация. У меня была таблица с IDEN PK, которая фрагментировалась на 99% из-за разбиения страниц на столбцах varchar. Очень активная таблица и по характеру приложения создается новая строка пустой строки, а затем заполняется. Чар исправил проблему фрагментации.
папараццо
12
@Jim McKeeth - эти вычисления верны, только если вы используете кодировку latin1. Поскольку большинство людей должны использовать utf8 в наши дни, ваши столбцы CHAR будут использовать в 3 раза больше пространства в среднем как VARCHAR, который хранит в основном символы в базовой многоязычной плоскости.
Гэвин Тоуи
11
@JimMcKeeth да, это правильно. Поскольку CHAR имеет фиксированную длину, он должен быть зафиксирован на максимально возможном пространстве, которое может быть использовано. В UTF8 это 3 байта на символ. Для varchar можно использовать 1-3 байта на символ по мере необходимости. Это в руководстве по MySQL: dev.mysql.com/doc/refman/5.0/en/charset-unicode-utf8.html
Гэвин Тоуи,
3
В чем разница со строками FooBar и varchar (100) против char (100)? Я думаю, что это лучше демонстрирует разницу, да? Нет?
Ненотлеп
4
@GavinTowey SQLSERVER использует UCS-2 для своих типов данных NCHAR и NVARCHAR. Это всегда два байта на символ.
1010
69

Если вы работаете со мной, и вы работаете с Oracle, я, вероятно, заставил бы вас использовать его varcharпрактически в любых обстоятельствах. Предположение, что charиспользуется меньше вычислительной мощности, чем varcharможет быть верно ... пока ... но движки баз данных со временем становятся лучше, и такого рода общее правило создает будущий "миф".

Другое дело: я никогда не видел проблемы с производительностью, потому что кто-то решил пойти с varchar. Вы будете гораздо лучше использовать свое время для написания хорошего кода (меньше обращений к базе данных) и эффективного SQL (как работают индексы, как оптимизатор принимает решения, почему existsбыстрее, чем inобычно ...).

Заключительная мысль: я видел все виды проблем с использованием CHAR, людей, ищущих «когда они должны искать», или людей, ищущих «FOO», когда они должны искать «FOO (куча пробелов здесь)» или люди, не обрезающие конечные пробелы, или ошибки с Powerbuilder, добавляющие до 2000 пробелов к значению, которое он возвращает из процедуры Oracle.

Итан Пост
источник
20
Я немного не согласен с вашим первым абзацем, поскольку char может дать подсказку, которая может быть полезна оптимизаторам, даже будущим, и может помочь сообщить о намерениях столбца. Но +1 для вашего третьего абзаца. Я ненавижу все лишние пробелы. Поле должно просто хранить все, что я в него вставляю, без всякого [объяснительного] заполнения. По сути, я просто использую char, если все данные должны быть одинаковой длины, не больше и не меньше, сейчас и навсегда. Конечно, это очень редко, и обычно это символ (1).
Джеффри Л Уитледж
char также дает подсказки аналитикам и разработчикам ... это число x символов ... Если они думают о сериализации в каком-то другом формате, это может быть полезно. (Я был вынужден хранить контрольную сумму md5 в char в mssql, который не имел типа uuid ... и я никогда не хотел ничего <32 байта ... также наложил ограничение на столбец).
Joefromct
31

В дополнение к преимуществам производительности, CHARможет использоваться, чтобы указать, что все значения должны быть одинаковой длины, например, столбец для сокращений штатов США.

Хэнк Гей
источник
Или коды стран - может помочь отличить использование 2- или 3-символьного сокращения кода страны
Дэн Филд
Если это действительно фиксированная длина, то должно быть ограничение, обеспечивающее это. Хотя, если вы используете CHAR, вы должны убедиться, что ваши ограничения скидки отступают.
jpmc26
18

Char немного быстрее, поэтому, если у вас есть столбец, который, как вы ЗНАЕТЕ, будет определенной длины, используйте char. Например, хранение (M) ale / (F) emale / (U), неизвестное для пола, или 2 символа для штата США.

Джаррет Мейер
источник
4
Не уверен, что это ОТЛИЧНЫЙ ответ, так как ENUM обычно имеет гораздо больше смысла, хотя я не уверен, насколько широко поддерживается этот тип (за пределами MySQL).
Бобби Джек,
Мне кажется, что множество состояний не обязательно является неизменным, поэтому char (2) кажется гораздо более подходящим, чем перечисление.
Кернс
1
@ Бобби Джек - я не знаю конкретных деталей какой-либо конкретной реализации перечисления SQL, но имейте в виду, что перечисление, хранящееся в виде 4-байтового целого числа, может потребовать больше места, чем столбец char (1) или char (2) с те же данные. В некотором смысле перечисления являются более логичными с точки зрения их интерпретации, и это может быть убедительным, но все в системе RDBMS является абстрактным на некотором уровне и подчиняется предикатам, определенным для таблиц.
Джеффри Л Уитледж
4
Плохой пример, ENUM лучше всего подходит для этого случая. Лучшим примером будет трехбуквенный код аэропорта ИАТА
Эндрю Джонсон
5
@ Андрей, не все db поддерживают типы данных ENUM. MSSQLServer, например, нет. Кроме того, ENUM, хранящийся как int, занимает 4 байта. CHAR (1) занимает 1 байт, а NCHAR (1) занимает 2 байта.
Джаррет Мейер
17

NChar или Char работают лучше, чем их альтернативные варианты?

Отличный вопрос Простой ответ - да в определенных ситуациях. Посмотрим, можно ли это объяснить.

Очевидно, мы все знаем, что если я создам таблицу со столбцом varchar (255) (назовем этот столбец myColumn) и вставим миллион строк, но поместим в myColumn только несколько символов для каждой строки, таблица будет намного меньше (в целом количество страниц данных, необходимых для механизма хранения), чем если бы я создал myColumn как char (255). Каждый раз, когда я выполняю операцию (DML) с этой таблицей и запрашиваю много строк, будет быстрее, когда myColumn varchar, потому что мне не нужно перемещаться вокруг всех этих «лишних» пробелов в конце. Перемещение, например, когда SQL Server выполняет внутреннюю сортировку, например, во время отдельной операции или операции объединения, или если он выбирает объединение во время плана запроса и т. Д.

Но есть некоторые накладные расходы при использовании varchar. SQL Server должен использовать двухбайтовый индикатор (служебные данные), чтобы в каждой строке узнать, сколько байтов содержится в этой строке myColumn. Проблема не в 2 дополнительных байтах, а в необходимости «декодировать» длину данных в myColumn в каждой строке.

По моему опыту имеет смысл использовать char вместо varchar для столбцов, к которым будут добавляться запросы. Например, первичный ключ таблицы или другой столбец, который будет проиндексирован. CustomerNumber в демографической таблице, или CodeID в таблице декодирования, или, возможно, OrderNumber в таблице заказов. Используя char, механизм запросов может быстрее выполнять объединение, потому что он может выполнять арифметику с прямым указателем (детерминистически) вместо того, чтобы перемещать указатели на переменное количество байтов при чтении страниц. Я знаю, что мог потерять тебя в последнем предложении. Объединения в SQL Server основаны на идее «предикатов». Предикат является условием. Например, myColumn = 1 или OrderNumber <500.

Поэтому, если SQL Server выполняет оператор DML, а предикаты или «ключи», к которым присоединяются, имеют фиксированную длину (символ), обработчику запросов не нужно выполнять столько работы, чтобы сопоставить строки из одной таблицы и строки из другой стол. Не нужно будет выяснить, как долго находятся данные в строке, а затем пройтись вниз по строке, чтобы найти конец. Все это требует времени.

Теперь имейте в виду, что это легко может быть плохо реализовано. Я видел char, используемый для полей первичного ключа в онлайн-системах. Ширина должна быть небольшой, то есть char (15) или что-то разумное. И это лучше всего работает в онлайн-системах, потому что вы, как правило, извлекаете или добавляете только небольшое количество строк, поэтому необходимость «тримировать» те конечные пробелы, которые вы получите в наборе результатов, является тривиальной задачей, а не объединением миллионов строки из одной таблицы в миллионы строк в другой таблице.

Еще одна причина, по которой CHAR имеет смысл по сравнению с varchar в онлайн-системах, заключается в том, что он уменьшает разбиение страниц. Используя char, вы, по сути, «резервируете» (и тратите) это пространство, поэтому, если пользователь приходит позже и помещает больше данных в этот столбец, SQL уже выделил для него пространство и он уходит.

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

Таким образом, это 3 способа, которыми онлайновая (OLTP) система может извлечь выгоду из char по сравнению с varchar. Я почти никогда не использую char в сценарии «хранилище / анализ / OLAP», потому что обычно у вас так много данных, что все эти столбцы char могут добавить много потерянного пространства.

Имейте в виду, что char может сделать вашу базу данных намного больше, но большинство инструментов резервного копирования имеют сжатие данных, поэтому ваши резервные копии имеют примерно такой же размер, как если бы вы использовали varchar. Например LiteSpeed ​​или RedGate SQL Backup.

Другое использование в представлениях, созданных для экспорта данных в файл фиксированной ширины. Допустим, мне нужно экспортировать некоторые данные в плоский файл для чтения мэйнфреймом. Это фиксированная ширина (без ограничения). Мне нравится хранить данные в моей «промежуточной» таблице как varchar (таким образом, занимая меньше места в моей базе данных), а затем использовать представление для CAST всего, что эквивалентно символу, с длиной, соответствующей ширине фиксированной ширины для этого столбца. , Например:

create table tblStagingTable (
pkID BIGINT (IDENTITY,1,1),
CustomerFirstName varchar(30),
CustomerLastName varchar(30),
CustomerCityStateZip varchar(100),
CustomerCurrentBalance money )

insert into tblStagingTable
(CustomerFirstName,CustomerLastName, CustomerCityStateZip) ('Joe','Blow','123 Main St Washington, MD 12345', 123.45)

create view vwStagingTable AS
SELECT CustomerFirstName = CAST(CustomerFirstName as CHAR(30)),
CustomerLastName = CAST(CustomerLastName as CHAR(30)),
CustomerCityStateZip = CAST(CustomerCityStateZip as CHAR(100)),
CustomerCurrentBalance = CAST(CAST(CustomerCurrentBalance as NUMERIC(9,2)) AS CHAR(10))

SELECT * from vwStagingTable

Это круто, потому что внутренне мои данные занимают меньше места, потому что они используют varchar. Но когда я использую DTS или SSIS или даже просто вырезал и вставлял из SSMS в Блокнот, я могу использовать представление и получить правильное количество конечных пробелов. В DTS у нас раньше была функция, черт побери, я думаю, она называлась «предложить столбцы» или что-то в этом роде. В SSIS вы больше не можете этого делать, вам нужно утомительно определять менеджер соединений с плоскими файлами. Но так как у вас есть настройки вида, SSIS может знать ширину каждого столбца и может сэкономить много времени при построении ваших задач потока данных.

Итак, суть ... используйте varchar. Существует очень мало причин использовать char, и это только из-за производительности. Если у вас есть система с сотнями миллионов строк, вы увидите заметную разницу, если предикаты являются детерминированными (char), но для большинства систем использование char просто тратит пространство.

Надеюсь, это поможет. Джефф

Джефф
источник
Вы говорите, что фиксированный чат занимает больше места не только при хранении, но и при транспортировке или "перемещении", как вы говорите? С БД сервера на мой клиент например? Когда мы теряем эти нулевые байты?
Красный горох
9

Есть преимущества в производительности, но здесь не упоминалось: миграция строк. С помощью char вы резервируете все пространство заранее. Итак, скажем, у вас есть char (1000), и вы храните 10 символов, вы будете использовать все 1000 символов charaters. В varchar2 (1000) вы будете использовать только 10 символов. Проблема возникает, когда вы изменяете данные. Допустим, вы обновили столбец, чтобы теперь он содержал 900 символов. Возможно, что пространство для расширения varchar не доступно в текущем блоке. В этом случае механизм БД должен перенести строку в другой блок и сделать указатель в исходном блоке на новую строку в новом блоке. Чтобы прочитать эти данные, движку БД теперь придется прочитать 2 блока.
Никто не может однозначно сказать, что varchar или char лучше. Существует пространство для временного компромисса и рассмотрения вопроса о том, будут ли данные обновляться, особенно если есть хорошие шансы на их рост.

Тони БенБрахим
источник
Я думаю, у вас есть опечатка в вашем посте - не должно ли varchar2 (1000) быть CHAR (1000)?
Мэтт Рогиш
8

Существует разница между ранней оптимизацией производительности и использованием правил, основанных на рекомендациях. Если вы создаете новые таблицы, в которых у вас всегда будет поле фиксированной длины, имеет смысл использовать CHAR, вам следует использовать его в этом случае. Это не ранняя оптимизация, а скорее внедрение практического правила (или лучшей практики).

т.е. - если у вас есть поле состояния из 2 букв, используйте CHAR (2). Если у вас есть поле с фактическими именами состояний, используйте VARCHAR.

Брайан Ребейн
источник
8

Я бы выбрал varchar, если в столбце не хранится фиксированное значение, например, код штата США - который всегда имеет длину 2 символа, а список допустимых кодов штатов США меняется не часто :).

В любом другом случае, даже при хранении хешированного пароля (фиксированной длины), я бы выбрал varchar.

Почему - столбец типа char всегда выполняется с пробелами, что делает столбец my_column определенным как char (5) со значением 'ABC' внутри сравнения:

my_column = 'ABC' -- my_column stores 'ABC  ' value which is different then 'ABC'

ложный.

Эта функция может привести к множеству раздражающих ошибок во время разработки и усложняет тестирование.

Гжегож Герлик
источник
1
По крайней мере, в MSSQL Server, 'abc' = 'abc'. Я никогда не понимал, нравится ли мне эта функция или не нравится ...
Марк Брэкет
Хорошее прочтение о набивке
Эдвард
6

CHAR занимает меньше места, чем VARCHAR, если все ваши значения данных в этом поле имеют одинаковую длину. Теперь, возможно, в 2009 году база данных объемом 800 ГБ для всех намерений и целей будет такой же, как и 810 ГБ, если вы конвертируете VARCHAR в CHAR, но для коротких строк (1 или 2 символа) CHAR по-прежнему остается «лучшей практикой» в отрасли, я бы сказал.

Теперь, если вы посмотрите на большое разнообразие типов данных, которые большинство баз данных предоставляют даже для одних целых чисел (bit, tiny, int, bigint), есть причины выбрать один из других. Простой выбор bigint каждый раз на самом деле немного неосведомлен о целях и использовании этой области. Если поле просто представляет возраст людей в годах, bigint является излишним. Теперь это не обязательно «неправильно», но это не эффективно.

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

Скотт Даффи
источник
4

Я поддерживаю комментарий Джима Маккита.

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

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

Но вы, вероятно, не будете заботиться о снижении производительности, если у вас огромный стол.

Помните мудрые слова Джикстры. Ранняя оптимизация производительности - корень всего зла.

Альваро Родригес
источник
4
В вашем комментарии есть степень спекуляции. Я снова и снова видел, как подобные предположения проверяются, и прямо противоположное оказывается верным. Проблема в том, что многие инженеры воспримут такую ​​информацию как Евангелие. Пожалуйста, ребята, создайте контрольные примеры, которые отражают ваши реальные ситуации.
Итан Пост
Итан абсолютно прав. Это зависит от используемой вами реализации, что без ссылок на фактические (Product, Version) это совершенно бесполезно.
Дэвид Шмитт
Когда вы обновляете CHARстолбец, индексы также должны обновляться. В этом отношении нет разницы в обновлении столбца VARCHAR или CHAR. Подумайте об обновлении FOOдо BAR.
a_horse_with_no_name
4

Многие люди отмечают, что если вы знаете точную длину значения, использование CHAR имеет некоторые преимущества. Но несмотря на то, что сохранение американских штатов в виде CHAR (2) сегодня прекрасно, когда вы получаете сообщение от продаж, что «Мы только что сделали нашу первую продажу в Австралию», вы находитесь в мире боли. Я всегда посылаю, чтобы переоценить, как долго, я думаю, поля должны быть, а не делать «точное» предположение, чтобы покрыть будущие события. VARCHAR даст мне больше гибкости в этой области.

Craig
источник
3

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

Интересная вещь, которую стоит отметить, когда дело доходит до DB Types, это то, что sqlite (популярная мини-база данных с довольно впечатляющей производительностью) помещает все в базу данных в виде строки и печатает на лету.

Я всегда использую VarChar и обычно делаю его намного больше, чем мне может понадобиться. Например. 50 для Имя, как вы говорите, почему бы просто не быть в безопасности.

Тоби аллен
источник
3

Я бы никогда не использовал символы. У меня были эти дебаты со многими людьми, и они всегда поднимают усталое клише, что символ быстрее. Ну, я говорю, насколько быстрее? О чем мы здесь говорим, миллисекундах, секундах и если да, то сколько? Вы говорите мне, потому что кто-то утверждает, что это на несколько миллисекунд быстрее, мы должны ввести в систему тонны трудно исправляемых ошибок?

Итак, вот некоторые проблемы, с которыми вы столкнетесь:

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

Теперь предположим, что у вас есть типичный пример поля char, состоящего всего из одного символа, но поле является необязательным. Если кто-то передает пустую строку в это поле, он становится одним пробелом. Поэтому, когда другое приложение / процесс запрашивает его, они получают один пробел, если они не используют rtrim. У нас были XML-документы, файлы и другие программы, отображающие только один пробел в необязательных полях и разбивающие вещи.

Итак, теперь вы должны убедиться, что вы передаете пустые строки, а не пустые, в поле char. Но это НЕ правильное использование нуля. Вот использование нуля. Допустим, вы получили файл от поставщика

Имя | Пол | Город

Боб || Лос-Анджелес

Если пол не указан, введите в таблицу Боба, пустую строку и Лос-Анджелес. Теперь предположим, что вы получили файл и его формат изменился, и пол больше не включен, но был в прошлом.

Имя | Город

Боб | Сиэтл

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

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

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

Мауро Торрес
источник
2

При вычислении фактического необходимого размера для значения столбца и выделении пространства для Varchar есть небольшие накладные расходы, поэтому, если вы точно уверены, как долго значение будет всегда, лучше использовать Char и избегать попадания.

Гай Старбак
источник
2

Это классический компромисс между производительностью и производительностью.

В MS SQL 2005 Varchar (или NVarchar для языков, требующих два байта на символ, т.е. китайский), имеют переменную длину. Если вы добавите в строку после того, как она была записана на жесткий диск, она поместит данные в неконкурентном месте в исходную строку и приведет к фрагментации ваших файлов данных. Это повлияет на производительность.

Итак, если пространство не является проблемой, то Char лучше для производительности, но если вы хотите уменьшить размер базы данных, тогда varchars лучше.

Лео Мур
источник
2

Фрагментация. Char резервирует пространство, а VarChar - нет. Разделение страницы может потребоваться для размещения обновления до varchar.

папараццо
источник
Из-за многих других факторов при обновлении CHARстолбца может произойти разделение страницы .
Рик Джеймс
1

при использовании значений varchar SQL Server требуется дополнительно 2 байта на строку для хранения некоторой информации об этом столбце, тогда как если вы используете char, это не нужно, если только вы

SQLMenace
источник
0

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

Из-за этого у вас нет экономии места при использовании VARCHAR (200) по сравнению с CHAR (200)

FlySwat
источник
3
Какие базы данных реализуют VARCHAR таким образом?
Троэльс Арвин
5
Серьезно, какая база данных реализует это таким образом? То, что вы описываете, обычно относится к CHAR, а не к VARCHAR.
Ричард Симойс
mysql преобразует символы varchar в символы, если в одной таблице есть символы char и varchar.
Malfist
Моя интерпретация комментариев MySQL заключается в том, что это не относится к хранилищу первичных таблиц, но может иметь отношение к временным таблицам, например. для группировки / сортировки данных.dev.mysql.com/doc/refman/8.0/ru/char.html stackoverflow.com/questions/262238/…
Томас W
0

Использование CHAR (NCHAR) и VARCHAR (NVARCHAR) приводит к различиям в способах хранения данных сервером базы данных. Первый вводит конечные пробелы; Я столкнулся с проблемой при использовании его с оператором LIKE в функциях SQL SERVER. Поэтому я должен сделать это безопасно, используя VARCHAR (NVARCHAR) все время.

Например, если у нас есть таблица TEST (ID INT, Status CHAR (1)) , и вы пишете функцию для вывода списка всех записей с некоторым конкретным значением, например, следующим:

CREATE FUNCTION List(@Status AS CHAR(1) = '')
RETURNS TABLE
AS
RETURN
SELECT * FROM TEST
WHERE Status LIKE '%' + @Status '%'

В этой функции мы ожидаем, что когда мы добавим параметр по умолчанию, функция вернет все строки, но на самом деле это не так. Измените тип данных @Status на VARCHAR, чтобы устранить проблему.

Туан Ле ПН
источник
Это также можно изменить с помощью ansi_padding Как получить значения
Эдвард