Что такое sequence в PostgreSQL?
Sequence в PostgreSQL
Sequence (последовательность) — это специальный объект схемы базы данных, который генерирует уникальные числа в заданном порядке. PostgreSQL гарантирует, что каждый вызов функции nextval() вернёт уникальное значение, даже в условиях параллельных транзакций.
Зачем нужны sequences
Главная цель — автоматически генерировать уникальные идентификаторы для строк таблицы. Это безопаснее и производительнее, чем вручную вычислять MAX(id) + 1, поскольку sequence работает вне транзакции и защищён от гонки данных.
Создание sequence
-- Создать последовательность вручную
CREATE SEQUENCE user_id_seq
START WITH 1
INCREMENT BY 1
MINVALUE 1
MAXVALUE 9223372036854775807
CACHE 1;
Основные функции для работы
-- Получить следующее значение (изменяет состояние)
SELECT nextval('user_id_seq');
-- Получить текущее значение (не изменяет состояние, только после вызова nextval в сессии)
SELECT currval('user_id_seq');
-- Установить значение счётчика вручную
SELECT setval('user_id_seq', 1000);
Привязка sequence к колонке таблицы
-- Явное использование sequence как значения по умолчанию
CREATE TABLE users (
id INTEGER DEFAULT nextval('user_id_seq') PRIMARY KEY,
name TEXT NOT NULL
);
Типы SERIAL и IDENTITY — синтаксический сахар
PostgreSQL предоставляет удобные псевдотипы, которые автоматически создают sequence под капотом:
-- SERIAL — устаревший, но широко распространённый способ
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
total NUMERIC
);
-- GENERATED ALWAYS AS IDENTITY — современный стандарт SQL (PostgreSQL 10+)
CREATE TABLE products (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT
);
SERIAL создаёт sequence и привязывает его через DEFAULT nextval(...). IDENTITY — более строгий стандарт SQL:2003, который не позволяет случайно вставить произвольное значение без явного OVERRIDING SYSTEM VALUE.
Важные свойства sequence
- Атомарность без транзакции:
nextval()не откатывается при откате транзакции — пропуски в нумерации это нормально. - CACHE: можно кэшировать несколько значений в памяти сессии для производительности, но это увеличивает риск пропусков при сбое.
- CYCLE / NO CYCLE: определяет, начнёт ли sequence отсчёт заново после достижения
MAXVALUE.
Просмотр существующих sequences
-- Список всех sequences в текущей схеме
SELECT sequence_name, start_value, increment
FROM information_schema.sequences
WHERE sequence_schema = 'public';
Что хочет услышать интервьюер
Понимание, что sequence — это объект схемы БД, а не просто свойство колонки
Знание основных функций: nextval(), currval(), setval()
Понимание разницы между SERIAL и GENERATED AS IDENTITY
Осознание того, что пропуски в sequence — нормальное поведение при откате транзакций
Понимание, зачем sequence нужен в условиях параллельных транзакций (защита от гонки)
Пример: Использование sequence через TypeORM
// Пример: создание таблицы с автоинкрементом через TypeORM (под капотом использует sequence)
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')
export class User {
// TypeORM генерирует SERIAL или IDENTITY в зависимости от настроек
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
// Эквивалентный SQL, который TypeORM выполнит:
// CREATE SEQUENCE users_id_seq START 1;
// CREATE TABLE users (
// id INTEGER DEFAULT nextval('users_id_seq') PRIMARY KEY,
// name TEXT NOT NULL
// );
Пример: Работа с sequence через node-postgres
// Пример: получить следующее значение sequence напрямую через pg (node-postgres)
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
async function getNextUserId(): Promise<number> {
const result = await pool.query("SELECT nextval('users_id_seq') AS next_id");
return Number(result.rows[0].next_id);
}
// Сброс счётчика после ручной вставки данных с явными ID
async function resetSequenceAfterImport(maxId: number): Promise<void> {
// setval с третьим параметром false означает: следующий nextval вернёт именно это значение
await pool.query(`SELECT setval('users_id_seq', $1, false)`, [maxId + 1]);
}
Типичные ошибки
Путают sequence с AUTO_INCREMENT из MySQL — в PostgreSQL это разные механизмы с разными гарантиями
Считают, что значения sequence всегда будут без пропусков — при откате транзакции значение не возвращается
Используют устаревший SERIAL вместо современного GENERATED AS IDENTITY
Не знают, что currval() работает только в той же сессии, где уже был вызван nextval()
Думают, что setval() опасно использовать в продакшене — на самом деле это стандартный способ сброса счётчика после bulk-вставки


