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

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

19 ответов


вообще забрать CHAR если все строки будут близки к такая же длина. Pick тип varchar когда внут существенно. Голец может быть немного быстрее, потому что все строки имеют одинаковую длину.

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

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

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

Примечание: Microsoft SQL имеет 2 байта накладные для varchar. Это может варьироваться от БД к БД, но обычно существует по крайней мере 1 байт накладных расходов, необходимых для указания длины или EOL на VARCHAR.

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


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

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

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


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


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


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

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

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

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

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

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

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

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

другая причина использования CHAR аналогична второй причине. Если программист или пользователь выполняет "пакетное" обновление до миллионов строк, добавьте некоторые например, вы не получите звонок от своего DBA посреди ночи, задаваясь вопросом, почему их диски заполнены. Другими словами, это приводит к более предсказуемому росту размера базы данных.

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

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

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

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, и это только по соображениям производительности. Если у вас есть система с hundrends миллионов строк вы увидите заметную разницу, если предикаты детерминированы (char), но для большинства систем, использующих char, просто тратят пространство.

надеюсь, что это поможет. Джефф!--2-->


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


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

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


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

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

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

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

false.

этой характеристика может привести ко многим раздражающим ошибкам во время разработки и затрудняет тестирование.


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

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

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


Я стою на комментарий Джим McKeeth по.

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

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

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

помните мудрые слова Djikstra по. Ранняя оптимизация-корень всех зол.


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

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

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


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


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

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

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


многие люди указали, что если вы знаете точную длину значения, используя CHAR имеет некоторые преимущества. Но в то время как сохранение штатов США как CHAR(2) отлично сегодня, когда вы получаете сообщение от продаж, что "мы только что сделали нашу первую продажу в Австралию", вы находитесь в мире боли. Я всегда посылаю, чтобы переоценить, как долго, я думаю, поля должны быть, а не делать "точную" догадку, чтобы покрыть будущие события. ВАРЧАР даст мне больше гибкости в этой области.


фрагментации. Запасы гольца пространства и varchar не. Разделение страниц может потребоваться для размещения обновления varchar.


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


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

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


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

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

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

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


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

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

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

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

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

Имя|Пол|Город Боб / / Лос-Анджелес

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

Имя|Город Боб / Сиэтл

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

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

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