PostgreSQLMiddleTechnical
Что такое enum и в чём его минус по сравнению с lookup-таблицей?
Enum фиксирует упорядоченный набор значений в типе PostgreSQL (легко сравнивать, компактно); lookup-таблица гибче — поддерживает FK, локализацию, управление из UI и удаление значений без DDL-миграции.
Enum в PostgreSQL
PostgreSQL enum — пользовательский тип данных с фиксированным упорядоченным набором строковых меток. Значения хранятся как 4-байтовый OID, занимают меньше места, чем text, и поддерживают сравнение и сортировку по порядку объявления.
-- Создать enum-тип и использовать в таблице
CREATE TYPE order_status AS ENUM ('new', 'paid', 'shipped', 'cancelled');
CREATE TABLE orders (
order_id bigserial PRIMARY KEY,
user_id bigint NOT NULL,
status order_status NOT NULL DEFAULT 'new',
created_at timestamptz NOT NULL DEFAULT now()
);
-- Запрос с фильтром и сортировкой по enum-порядку
SELECT order_id, status
FROM orders
WHERE status > 'new' -- работает по порядку объявления
ORDER BY status;
Изменение enum — DDL-операции
-- Добавить новое значение (PostgreSQL 9.1+, не требует rewrite таблицы):
ALTER TYPE order_status ADD VALUE 'refunded' AFTER 'cancelled';
-- Переименовать значение (PostgreSQL 10+):
ALTER TYPE order_status RENAME VALUE 'new' TO 'pending';
-- Удалить значение — НЕЛЬЗЯ напрямую!
-- Нужно: создать новый тип, мигрировать данные, дропнуть старый.
Lookup-таблица — альтернатива
-- Нормализованный вариант через reference table
CREATE TABLE order_statuses (
status_code text PRIMARY KEY,
label_ru text NOT NULL,
label_en text NOT NULL,
sort_order smallint NOT NULL,
is_terminal boolean NOT NULL DEFAULT false
);
INSERT INTO order_statuses VALUES
('new', 'Новый', 'New', 1, false),
('paid', 'Оплачен', 'Paid', 2, false),
('shipped', 'Отправлен','Shipped', 3, false),
('cancelled', 'Отменён', 'Cancelled', 4, true);
CREATE TABLE orders (
order_id bigserial PRIMARY KEY,
user_id bigint NOT NULL,
status text NOT NULL DEFAULT 'new'
REFERENCES order_statuses(status_code),
created_at timestamptz NOT NULL DEFAULT now()
);
Когда выбирать enum, когда lookup-таблицу
- Enum — набор значений стабилен (технические состояния: pending/active/deleted), нет дополнительных атрибутов, не нужна локализация, изменение схемы синхронизируется с релизом кода.
- Lookup-таблица — значения управляются через UI/admin-панель, нужны локализованные labels, sort_order, флаги (is_terminal, is_visible), история изменений или внешние ключи из других таблиц.
Подводные камни
- Удалить значение из enum нельзя без создания нового типа и полной миграции таблицы — это блокирующая операция на продакшне.
- Добавление значения через
ADD VALUEне транзакционно до PostgreSQL 12 — откат транзакции не откатывает изменение типа. - Enum жёстко связывает релиз приложения и миграцию базы: добавили новое значение в enum — нужно деплоить код, умеющий его обрабатывать, одновременно.
- ORM и клиентские библиотеки могут кэшировать список значений enum при старте — после
ADD VALUEнужен рестарт приложения или очистка кэша соединений. - Enum не поддерживает FK к другой таблице — нельзя хранить дополнительные атрибуты на уровне базы без дополнительной таблицы.
- Lookup-таблица добавляет JOIN к каждому запросу — для hot path с высокой нагрузкой может потребоваться кэширование справочника в приложении.
- При использовании enum в партиционированной таблице изменение типа затрагивает все партиции — потенциально длительная операция.
Common mistakes
- Использовать enum для бизнес-справочника.
- Не учитывать сложность удаления значений.
- Забывать про порядок enum-значений.
What the interviewer is testing
- Просит пример стабильного enum.
- Проверяет migration trade-off.
- Уточняет, когда нужен FK на справочник.