Индекс производительности для CHAR против VARCHAR (Postgres)

16

В этом ответе ( /programming/517579/strings-as-primary-keys-in-sql-database ) одно замечание привлекло мое внимание:

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

Это применимо / все еще применяется к Postgres?

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

LetMeSOThat4U
источник

Ответы:

24

CHARи VARCHARреализованы точно так же в Postgres (и Oracle). Нет разницы в скорости при использовании этих типов данных.

Однако есть одно отличие, которое может повлиять на производительность: charстолбец всегда дополняется до определенной длины. Таким образом, если вы определяете столбец как char(100)и один как, varchar(100)но сохраняете только 10 символов в каждом, char(100)столбец использует 100 символов для каждого значения (10 сохраненных символов плюс 90 пробелов), тогда как в varcharстолбце хранится только 10 символов.

Сравнение 100 символов с 100 символами будет медленнее, чем сравнение 10 символов с 10 символами - хотя я сомневаюсь, что вы действительно можете измерить эту разницу в запросе SQL.

Если вы объявляете оба с длиной 10 символов и всегда сохраняете в них ровно 10 символов, то нет абсолютно никакой разницы (это верно для Oracle и Postgres)

Таким образом, единственное отличие - это заполнение, которое делается для charтипа данных.


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

Приведенная выше цитата верна только в том случае, если (и только если) charстолбец определен слишком широко (т. Е. Вы тратите пространство из-за заполнения). Если длина charстолбца всегда используется полностью (поэтому заполнение не происходит), приведенная выше цитата неверна (по крайней мере, для Postgres и Oracle)


С моей точки зрения, charтип данных на самом деле не имеет никакого реального использования слова. Просто используйте varchar(или textв Postgres) и забудьте, что charсуществует.

a_horse_with_no_name
источник
2
Сравнение 100 символов с 100 символами будет медленнее, чем сравнение 10 символов с 10 символами - хотя я сомневаюсь, что вы действительно можете измерить эту разницу в запросе SQL. - В зависимости от того, что делает запрос в дополнение к сортировке, разница может быть огромной. Вот почему в Postgres 9.5 появилась новая функция «сокращенных ключей»: pgeoghegan.blogspot.de/2015/01/…
chirlu
6

Я согласен со всем, что говорит a_horse_with_no_name, и в целом согласен с комментарием Эрвина:

Нет, чарс уступает (и устарел). текст и varchar выполняют (почти) одно и то же.

Метаданные

За одним небольшим исключением, я использую только один раз char(), когда я хочу, чтобы метаданные сказали, что это ДОЛЖНО иметь x-символы. Хотя я знаю, что char()жалуется , только если входное значение превышает предел, я буду часто защищать от недогрузок в CHECKограничении. Например,

CREATE TABLE foo (
  x char(10) CHECK ( length(x) = 10 )
);
INSERT INTO foo VALUES (repeat('x', 9));

Я делаю это по нескольким причинам,

  1. char(x)иногда определяется загрузчиками схемы как столбец с фиксированной шириной. Это может иметь значение в языке, который оптимизирован для строк фиксированной ширины.
  2. Он устанавливает соглашение, которое имеет смысл и легко исполняется. Я могу написать загрузчик схемы на языке, чтобы сгенерировать код из этого соглашения.

Нужен пример, где я могу это сделать,

  1. Двухбуквенные сокращения состояний, хотя этот список можно перечислить, я обычно делаю это с помощью ENUM.
  2. Идентификационные номера транспортных средств
  3. Номера моделей (фиксированного размера)

На ошибки

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

test=# INSERT INTO foo VALUES (repeat('x', 9));
ERROR:  new row for relation "foo" violates check constraint "foo_x_check"
DETAIL:  Failing row contains (xxxxxxxxx ).
test=# INSERT INTO foo VALUES (repeat('x', 11));
ERROR:  value too long for type character(10)

Контраст с varchar

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

  • Если спецификация имеет статическую ширину поля , которое я доверяю, я использую char(n),
  • В противном случае я использую textчто эффективно varchar(без ограничений)

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

Дополнительные замечания

Связанные вопросы и ответы:

Эван Кэрролл
источник
1

Postgresql

sales_reporting_db=# create table x (y char(2));
CREATE TABLE
sales_reporting_db=# insert into x values ('Y');
INSERT 0 1
sales_reporting_db=# select '*' || y || '*' from x;
 ?column? 
----------
 *Y*

оракул

SQL> create table x ( y char(2));

Table created.

SQL> insert into x values ('Y');

1 row created.

SQL> select '*' || y || '*' from x;

'*'|
----
*Y *

Postgresql не дополнял пробелами.

user939857
источник
Это всего лишь оптическая иллюзия в Postgres. ПопробуйтеSELECT pg_column_size(y) FROM x;
Дезсо
-2

Я нашел это самое полезное и быстрое объяснение в 3 строки:

От CHAR (n) против VARCHAR (N) против текста в Postgres

  • Если вы хотите сохранить некоторый текст с неизвестной длиной, используйте TEXTтип данных.
  • Если вы хотите сохранить некоторый текст с неизвестной длиной, но вы знаете максимальную длину, используйте VARCHAR(n).
  • Если вы хотите сохранить некоторый текст с известной точной длиной, используйте CHAR(N).
Льюис
источник