Числа мы разложили по коробкам в уроке про числовые типы. Но в книжном магазине больше всего не чисел, а слов и времени: названия книг, имена покупателей, момент заказа, срок доставки. С текстом в 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 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.