powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Varchar vs Text
5 сообщений из 5, страница 1 из 1
Varchar vs Text
    #39172029
Nitro_Junkie
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Нарвался недавно на такую неприятную штуку. Есть такой элементарный запрос:

Код: sql
1.
SELECT t0.Purchase_seriesNumber_UserInvoice FROM Purchase_userInvoice t0 WHERE (CAST('ПИ1111111' AS text)=t0.Purchase_seriesNumber_UserInvoice)



Код: sql
1.
2.
3.
4.
5.
"Seq Scan on purchase_userinvoice t0  (cost=0.00..27994.32 rows=2373 width=22) (actual time=1243.511..1243.511 rows=0 loops=1)"
"  Filter: ('ПИ1111111'::text = (purchase_seriesnumber_userinvoice)::text)"
"  Rows Removed by Filter: 474579"
"Planning time: 0.096 ms"
"Execution time: 1243.529 ms"



Очевидно не хватало индекса. Добавил, план не изменился. Чуть-чуть поменял запрос:

Код: sql
1.
SELECT t0.Purchase_seriesNumber_UserInvoice FROM Purchase_userInvoice t0 WHERE (CAST('ПИ1111111' AS varchar)=t0.Purchase_seriesNumber_UserInvoice)



План:
Код: sql
1.
2.
3.
4.
5.
"Index Only Scan using purchase_seriesnumber_userinvoice_key0_idx_purchase_userinvoice on purchase_userinvoice t0  (cost=0.42..0.68 rows=1 width=22) (actual time=1.145..1.145 rows=0 loops=1)"
"  Index Cond: (purchase_seriesnumber_userinvoice = 'ПИ1111111'::bpchar)"
"  Heap Fetches: 0"
"Planning time: 0.084 ms"
"Execution time: 1.158 ms"



Тип seriesNumber - char(20). План правильный при cast'е к чему угодно, кроме text.

"PostgreSQL 9.4.5, compiled by Visual C++ build 1800, 64-bit"

Собственно 2 вопроса:

1) Что такого волшебного в text?
2) И если вместо text использовать varchar какие могут быть подводные камни?
...
Рейтинг: 0 / 0
Varchar vs Text
    #39176790
kkv79
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
а я всегда varchar использую....
и как то побаиваюсь использовать text, а то вдруг подводный камень вылезет )))
...
Рейтинг: 0 / 0
Varchar vs Text
    #39176843
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_Junkie,

В данном случае вам скорее всего скажут: “а зачем вы константу к типу явно приводите? Оставьте как есть”

Но вопрос интересный, потому спросил в списке, отпишусь как ответят.

Если глянуть на то, как типы приводятся, то и `text`, и `varchar` "двоично совместимы" с типом `bpchar`:
Код: sql
1.
2.
3.
4.
5.
6.
7.
select c.*, tfrom.typname type_from, tto.typname type_to, f.proname 
  from pg_cast c 
  join pg_type tfrom on c.castsource = tfrom.oid 
  join pg_type tto on c.casttarget = tto.oid 
  left join pg_proc f on c.castfunc = f.oid
 where tfrom.typname in ('bpchar', 'text', 'varchar')
 order by tfrom.typname;


При этом `varchar` работает "как хочется".
Возможно связано с тем, что и `varchar`, и `bpchar` принимают ограничители длины, в отличии от `text`а.
...
Рейтинг: 0 / 0
Varchar vs Text
    #39176979
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_Junkie,

Не используйте char(n) типы, вы там еще много граблей с ними соберете совершенно неожиданных.
Да и смысла в них ровно ноль.

--
Maxim Boguk
www.postgresql-consulting.ru
...
Рейтинг: 0 / 0
Varchar vs Text
    #39178647
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_Junkie,

http://www.postgresql.org/message-id/flat/CAGnEbohnFkRWrW1rAiZhobGhabwUNGBJxFwGMAVDGzQfpp5bDw@mail.gmail.com]Вот тут обсуждение. И нужен раздел 10.2 документации , с которым будем работать, часть`Operator Type Resolution` (первая сверху).
Я смоделировал ситуацию так:
Код: sql
1.
2.
3.
4.
5.
6.
CREATE TABLE t(t_id int4, sn_c char(20));
INSERT INTO t
  SELECT id, chr((random()*26)::int4+65)||chr((random()*26)::int4+65)||((random()*99999)::int4+1)
    FROM generate_series(1, 10000) id;
CREATE INDEX i_t_sn_c ON t(sn_c);
VACUUM ANALYZE t;


Рассматриваем такой запрос:
Код: sql
1.
EXPLAIN (analyze, costs off) SELECT sn_c FROM t WHERE sn_c = 'AB1234';


Выражение имеет форму <left_arg> <op> <right_arg>, в нашем случае <op> = `=`. В документации (пункт первый) сказано, что если оператор не был задан явно ( раздел 4.2.5 ), то рассматриваются все видимые операторы с таким именем (возможно, вы создали оператор равенства в своей схеме? — он таже будетрассматриваться).

Далее начинается проверка типов.
Ищется оператор, который полностью соответствует типу данных аргументов. Если такой есть — используется.
Если один из аргументов имеет тип `unknown`, то считается что он равен типу второго аргумента — ищется оператор и используется.

Интересное начинается, если типы аргументов отличаются и нет соответствующего оператора сравнения (это как раз наш случай). Postgres должен привести оба аргумента к одному типу. Надо выбрать один из типов, `text` или `bpchar` (blank-padded char).
Посмотрите на результат запроса и скажите, к какому типу Postgres будет приводить:
Код: sql
1.
SELECT typname, typcategory, typispreferred FROM pg_type WHERE typname IN ('text', 'varchar', 'bpchar');


Изменение типа колонки (по сути — вызов функции) делает использование индекса невозможным.

Это отвечает на ваш вопрос #1 — тип `text` является предпочтительным и как только он появляется, то все аргументы (при необходимости) будут приведены к нему.

Касаемо типов категории `S` — они (почти все) хранятся одинаково, и обрабатываются одним движком:
Код: sql
1.
SELECT typname, typlen, typbyval, typalign, typstorage FROM pg_type WHERE typname IN ('text', 'bpchar', 'varchar', 'char');


Но у них должны быть различия на уровне SQL:
- text — универсальный
- varchar — тот же `text`, только может иметь ограничение длины
- bpchar — тот же `varchar`, только должен дополнятся пробелами до указанной длины.

Тип `char`стоит отдельно, его надо рассматривать как просто 8-битный символ, который храниться "как есть" (typlen=1, typstorage=p). Полагаю, это историческая часть системы и правильно работать с UTF8 в этом типе невозможно.
Не стоит путать тип данных Postgres'а `char` с типом данных SQL'а `char`! — когда вы создаёте колонку типа `char` в SQL'е, то Postgres использует `bpchar`.

TL;DR — В результате получается:
- используя явное преобразование к `text`, которое не соответствует типу колонки, вы склоняете Postgres к тем последствиям, что вы и описали (ССЗБ);
- лучше оставлять константы без явного преобразования (в тех местах, где это допустимо) — база сама привёдет `unknown` к типу второго аргумента оператора
- т.к. SQL-тип `char` использует Postgres-тип `bpchar`, обрабатываемый одинаково с `text`-ом, то предпочтительней использовать `text`. Мало ли какие ещё сюопризы откопаете в связи с тем, что результат "должен" дополнятся пробелами (в кавычках, т.к. везде ли он дополняется?)
- если нужно ограничить длину `text` колонки — пользуйтесь CHECK constraint или сделайте свой DOMAIN.

Как-то так.
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Varchar vs Text
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]