Раздел 33 · PostgreSQL с нуля

Текст и даты: varchar, text, timestamp, interval

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Числа мы разложили по коробкам в уроке про числовые типы. Но в книжном магазине больше всего не чисел, а слов и времени: названия книг, имена покупателей, момент заказа, срок доставки. С текстом в Postgres всё проще, чем кажется. Со временем сложнее, чем кажется. Начнём с простого.

Текст: text, varchar, char

В Postgres три текстовых типа:

  • text: строка любой длины;
  • varchar(n): строка не длиннее n символов;
  • char(n): строка ровно из n символов, короткие дополняются пробелами.

Новички часто думают, что varchar(255) экономнее, чем text, как маленькая коробка экономнее большой. В Postgres это не так. Все три типа хранятся одинаково: сколько символов, столько и места, плюс пара байт на длину. По скорости разницы тоже нет.

Тогда зачем varchar(n)? Только как правило: «длиннее нельзя». Промокод в магазине не длиннее 12 символов, и база проследит:

CREATE TABLE book_orders (
  promo_code varchar(12)
);

INSERT INTO book_orders (promo_code) VALUES ('SUPERSALE2026');
ERROR:  value too long for type character varying(12)

Отсюда простое правило: по умолчанию text. varchar(n) там, где ограничение длины это настоящее бизнес-правило. char(n) не бери: дополнение пробелами создаёт больше сюрпризов, чем пользы.

И одна ловушка с длиной. Явное приведение к varchar(n) не падает, а молча обрезает строку:

SELECT 'Совершенный код'::varchar(10);
  varchar
------------
 Совершенны

Вставка в колонку падает с ошибкой, а приведение режет молча. Так записано в стандарте SQL.

Пара полезных операций со строками:

SELECT upper('пиши, сокращай'),
       'Грокаем' || ' ' || 'алгоритмы',
       length('Ёж'),
       'Грокаем алгоритмы' LIKE 'Грокаем%';
     upper      |     ?column?      | length | ?column?
----------------+-------------------+--------+----------
 ПИШИ, СОКРАЩАЙ | Грокаем алгоритмы |      2 | t

|| склеивает строки, length считает символы (а не байты, кириллица тут не помеха), LIKE проверяет шаблон, где % значит «что угодно».

Время: какие бывают типы

Теперь сложная часть. Время в базе бывает четырёх видов:

ТипЧто хранитПример
dateкалендарный день2026-10-09
timestampдень и время без часового пояса2026-10-09 14:30:00
timestamptzмомент времени, привязанный к поясу2026-10-09 14:30:00+03
intervalпромежуток3 days 02:00:00

С date всё понятно: дата рождения, срок доставки, день без часов.

Разницу между timestamp и timestamptz покажет записка «созвонимся в 12:00». Если ты в Москве, а коллега во Владивостоке, записка бесполезна: чьи 12:00? Это timestamp: время «как на стене», без указания, на какой стене висят часы.

А теперь запись «созвонимся в 12:00 по Москве». Её каждый переведёт на свои часы, и все позвонят одновременно. Это timestamptz: конкретный момент на планете.

Внутри timestamptz хранит время в UTC, а показывает в поясе текущей сессии. Пояс задаётся настройкой timezone:

SET timezone = 'Europe/Moscow';
SELECT TIMESTAMPTZ '2026-10-09 12:00:00+03';

SET timezone = 'Asia/Vladivostok';
SELECT TIMESTAMPTZ '2026-10-09 12:00:00+03';
      timestamptz
------------------------
 2026-10-09 12:00:00+03

      timestamptz
------------------------
 2026-10-09 19:00:00+10

Момент один, показан по-разному. А вот timestamp суффикс пояса молча выбросит:

SELECT TIMESTAMP '2026-10-09 12:00:00+03';
      timestamp
---------------------
 2026-10-09 12:00:00

Информация о поясе потеряна навсегда. Отсюда главное правило урока: момент события (заказ создан, оплата прошла) храни в timestamptz. Название путает: он не хранит пояс, он хранит точный момент, а заказам нужен именно момент. Простой timestamp оставь для случаев «в 9 утра по местному времени, где бы ты ни был», а таких в магазине почти нет.

Текущий момент даёт функция now(), она возвращает timestamptz. Сегодняшнюю дату даёт current_date.

урок из курса

Урок из курса «PostgreSQL с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.