В чём разница между колонками SERIAL и GENERATED AS IDENTITY?
SERIAL — устаревший псевдотип, создающий независимую последовательность; GENERATED AS IDENTITY (SQL:2003) жёстко привязывает последовательность к колонке через системный каталог и поддерживает OVERRIDING SYSTEM VALUE.
SERIAL vs GENERATED AS IDENTITY
SERIAL — это псевдотип, появившийся в ранних версиях PostgreSQL. При его использовании движок неявно создаёт отдельную последовательность (SEQUENCE) и устанавливает на колонку дефолт через DEFAULT nextval('...'). Колонка при этом имеет тип integer, а связь между ней и последовательностью существует лишь через именование — никакой настоящей зависимости нет.
GENERATED AS IDENTITY (SQL:2003, поддерживается с PostgreSQL 10) — это стандартный SQL-синтаксис. Последовательность создаётся автоматически и жёстко привязана к колонке через системный каталог (pg_depend). При удалении колонки последовательность удаляется вместе с ней. Поддерживаются два варианта:
GENERATED ALWAYS AS IDENTITY— PostgreSQL всегда генерирует значение; вставить произвольное число можно только явно указавOVERRIDING SYSTEM VALUE.GENERATED BY DEFAULT AS IDENTITY— можно передать своё значение без дополнительных оговорок; поведение схоже сSERIAL.
-- Старый способ
CREATE TABLE orders_old (
id SERIAL PRIMARY KEY,
amount numeric
);
-- Современный способ (рекомендуется)
CREATE TABLE orders_new (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
amount numeric
);
-- Посмотреть привязанную последовательность
SELECT pg_get_serial_sequence('orders_new', 'id');
-- Результат: pg_catalog.orders_new_id_seq
-- Принудительная вставка с OVERRIDING
INSERT INTO orders_new (id, amount)
OVERRIDING SYSTEM VALUE
VALUES (999, 100.50);
-- Изменить параметры последовательности через ALTER
ALTER TABLE orders_new
ALTER COLUMN id
SET GENERATED BY DEFAULT
SET START WITH 1000
RESTART WITH 1000;
Ключевые практические отличия:
- Управление последовательностью: у
SERIALпоследовательность живёт отдельно и может случайно «потеряться» приDROP COLUMN— последовательность при этом остаётся. УIDENTITYзависимость зарегистрирована в каталоге и каскадно удаляется. - Права: для
SERIALприходится отдельно выдаватьGRANT USAGE ON SEQUENCE; сIDENTITYэто не нужно — привилегии на таблицу охватывают и последовательность. - pg_dump: дамп
SERIALвоспроизводитCREATE SEQUENCE+DEFAULT nextval, тогда какIDENTITYсериализуется чище через стандартный синтаксис. - Переносимость:
GENERATED AS IDENTITYявляется частью стандарта ISO SQL и понятен другим СУБД (Oracle, DB2, MariaDB 10.3+).
Подводные камни
- При копировании таблицы через
CREATE TABLE ... AS SELECTилиpg_dump --schema-onlyатрибутIDENTITYне переносится автоматически — нужно явно пересоздавать колонку. INSERT ... OVERRIDING SYSTEM VALUEне обновляет «водяной знак» последовательности; после ручной вставки большого ID вызовитеALTER SEQUENCE ... RESTART, иначе получите конфликт уникальности.SERIALсоздаёт последовательность в той же схеме, что и таблица; при смене схемы (ALTER TABLE SET SCHEMA) последовательность остаётся в старой схеме — неочевидная бомба.- У
GENERATED ALWAYSнельзя вставитьDEFAULTчерез ORM без явногоOVERRIDING— некоторые ORM (ранние версии SQLAlchemy) не справлялись с этим сценарием. - Последовательности не транзакционны: «дырки» в значениях неизбежны при откате транзакций; не используйте ID для нумерации документов.
- При репликации через логическое декодирование
IDENTITY-колонки ведут себя корректно, но на подписчике нужно явно устанавливатьREPLICA IDENTITY, иначеUPDATE/DELETEупадут. bigserial/BIGINT GENERATED AS IDENTITYпредпочтительнееserialдля таблиц с высокой нагрузкой — диапазонintegerисчерпывается при ~2.1 млрд строк.
Common mistakes
- Отвечать про serial и generated as identity как про абстрактный SQL без учета MVCC, WAL, planner или конкретной версии PostgreSQL.
- Переносить поведение MySQL или ClickHouse на PostgreSQL без проверки документации.
- Не связывать механизм с последствиями для индексов, транзакций, блокировок или эксплуатации.
- Показывать синтаксис, который не поддерживается PostgreSQL.
What the interviewer is testing
- Кандидат объясняет serial и generated as identity через реальный механизм PostgreSQL, а не общими словами.
- Приводит корректный SQL или диагностический запрос для этой СУБД.
- Называет ограничения, версионные отличия или эксплуатационные последствия.
- Связывает ответ с проектированием приложения, производительностью, надежностью или безопасностью.