SQL SEQUENCE в PostgreSQL: один генератор ID для нескольких таблиц
Когда требуется генерировать уникальные идентификаторы в PostgreSQL, первое что приходит в голову — это SERIAL. Этот механизм прост в использовании и отлично справляется с задачей в большинстве случаев. Но что делать, если возникает задача посложнее?
Представим ситуацию: нужно создать систему нумерации документов, где номера должны быть общими для нескольких таблиц.
Например, счета, накладные и акты должны нумероваться из единого числового ряда: 100001, 100002, 100003... Попытка использовать отдельный SERIAL для каждой таблицы приведёт к дублированию номеров, а это недопустимо 🚫
Именно здесь на сцену выходят SEQUENCE — мощный инструмент для генерации уникальных идентификаторов с гибкостью, которую не может предоставить SERIAL.
Что такое SEQUENCE?
SEQUENCE — это специальный объект базы данных, который генерирует упорядоченную серию уникальных числовых значений по определённому правилу.
Представьте себе автоматический нумератор документов в офисе: каждый раз, когда приходит новый документ, нумератор выдаёт следующий номер. SEQUENCE работает точно так же, но в базе данных.
Простой пример: у вас есть счётчик, который стоит на значении 1000. Когда вы просите "дай следующее число", он выдаёт 1000 и увеличивается до 1001. В следующий раз он выдаст 1001, затем 1002, и так далее. Просто и надёжно! ✨
Зачем нужны SEQUENCE?
SEQUENCE даёт гораздо больше контроля и гибкости, чем SERIAL. Вот ключевые преимущества:
-
🔗 Независимость от таблиц
Один SEQUENCE можно использовать для генерации ID сразу в нескольких таблицах. Например, счета, накладные и акты могут нумероваться из единого числового ряда — это гарантирует уникальность номеров документов во всей системе. -
🎨 Гибкость настройки
Можно начать с любого числа (например, 1000), использовать обратный отсчёт, настроить циклическую нумерацию или изменить шаг приращения.
Создание SEQUENCE
SEQUENCE создаётся с помощью команды CREATE SEQUENCE.
Базовый синтаксис
PostgreSQL 17.5CREATE SEQUENCE имя_последовательности [START WITH начальное_значение] [INCREMENT BY шаг_приращения] [MINVALUE минимальное_значение] [MAXVALUE максимальное_значение] [CYCLE | NO CYCLE] [CACHE количество];
Параметры SEQUENCE
Например, SEQUENCE для генерации номеров заказов, начиная с 1000, может выглядеть так:
PostgreSQL 17.5CREATE SEQUENCE order_id_seq START WITH 1000 INCREMENT BY 1 NO CYCLE;
Функции для работы с SEQUENCE
PostgreSQL предоставляет несколько функций для работы с SEQUENCE:
nextval() — получить следующее значение
PostgreSQL 17.5SELECT nextval('order_id_seq');
Эта функция увеличивает счётчик и возвращает новое значение. ⬆️
При повторном вызове:
PostgreSQL 17.5SELECT nextval('order_id_seq');
currval() — получить текущее значение
PostgreSQL 17.5SELECT currval('order_id_seq');
Возвращает последнее значение, полученное через nextval() в текущей сессии, без изменения счётчика. 👀
⚠️ Важная ловушка! При первом использовании currval() можно получить ошибку "currval of sequence is not yet defined in this session". Причина: currval() можно вызвать только после хотя бы одного вызова nextval() в той же сессии!
Логика проста: нельзя узнать "текущее" значение, если оно ещё ни разу не было запрошено в рамках текущей сессии. 🤔
setval() — установить значение
PostgreSQL 17.5SELECT setval('order_id_seq', 5000);
Устанавливает текущее значение SEQUENCE. Следующий вызов nextval() вернёт 5001. 🎯
Применение в таблицах
Способ 1: Использование DEFAULT
Самый распространённый способ — использовать SEQUENCE как значение по умолчанию:
PostgreSQL 17.5CREATE TABLE Orders ( order_id INTEGER DEFAULT nextval('order_id_seq') PRIMARY KEY, customer_name VARCHAR(100), order_date DATE );
При вставке записи значение order_id будет автоматически заполнено:
PostgreSQL 17.5INSERT INTO Orders (customer_name, order_date) VALUES ('Иван Петров', '2025-11-14');
Способ 2: Тип SERIAL
PostgreSQL предлагает удобный тип SERIAL, который автоматически создаёт SEQUENCE: 💡
PostgreSQL 17.5CREATE TABLE Products ( product_id SERIAL PRIMARY KEY, product_name VARCHAR(100), price DECIMAL(10, 2) );
Это эквивалентно:
PostgreSQL 17.5CREATE SEQUENCE products_product_id_seq; CREATE TABLE Products ( product_id INTEGER DEFAULT nextval('products_product_id_seq') PRIMARY KEY, product_name VARCHAR(100), price DECIMAL(10, 2) );
Управление SEQUENCE
-
Просмотр информации о SEQUENCE
PostgreSQL 17.5SELECT * FROM information_schema.sequences WHERE sequence_name = 'order_id_seq';Или более детально через системную функцию:
PostgreSQL 17.5SELECT last_value, is_called FROM order_id_seq; -
Изменение параметров SEQUENCE
PostgreSQL 17.5ALTER SEQUENCE order_id_seq INCREMENT BY 10 RESTART WITH 2000; -
Удаление SEQUENCE
PostgreSQL 17.5DROP SEQUENCE order_id_seq;⚠️ Внимание: Если SEQUENCE используется в таблице как значение по умолчанию, сначала нужно удалить это ограничение или использовать CASCADE:
PostgreSQL 17.5DROP SEQUENCE order_id_seq CASCADE;
SEQUENCE для нескольких таблиц
Один SEQUENCE можно использовать для нескольких таблиц. Это особенно полезно для систем документооборота, где нужна сквозная нумерация:
PostgreSQL 17.5CREATE SEQUENCE document_number_seq START WITH 100000; CREATE TABLE Invoices ( invoice_id SERIAL PRIMARY KEY, document_number INTEGER DEFAULT nextval('document_number_seq') UNIQUE, customer_id INTEGER, amount DECIMAL(10, 2) ); CREATE TABLE DeliveryNotes ( note_id SERIAL PRIMARY KEY, document_number INTEGER DEFAULT nextval('document_number_seq') UNIQUE, order_id INTEGER, delivery_date DATE ); CREATE TABLE Acts ( act_id SERIAL PRIMARY KEY, document_number INTEGER DEFAULT nextval('document_number_seq') UNIQUE, contract_id INTEGER, act_date DATE );
Теперь все документы (счета, накладные, акты) будут иметь уникальные номера во всей системе документооборота. 🌐
Интерактивная схема работы SEQUENCE
Продвинутые техники
-
SEQUENCE с кэшированием
Для повышения производительности можно использовать кэширование:
PostgreSQL 17.5CREATE SEQUENCE fast_seq START WITH 1 INCREMENT BY 1 CACHE 100;База данных предварительно выделит 100 значений в памяти, что ускорит их выдачу. ⚡
-
Циклический SEQUENCE
Для случаев, когда нужна циклическая нумерация (например, дни недели):
PostgreSQL 17.5CREATE SEQUENCE day_of_week_seq START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 7 CYCLE;После достижения 7 SEQUENCE вернётся к 1. 🔄
-
Убывающий SEQUENCE
PostgreSQL 17.5CREATE SEQUENCE countdown_seq START WITH 100 INCREMENT BY -1 MINVALUE 1 NO CYCLE;Этот SEQUENCE будет генерировать: 100, 99, 98, 97... ⬇️
На что обратить внимание
-
В 90% случаев вам достаточно SERIAL
Не создавайте отдельный SEQUENCE, если вам не нужна его специфическая гибкость. SERIAL проще и справляется с большинством задач. -
Последовательности имеют пропуски 🕳️
Не ждите, что номера будут идти без пробелов: 1, 2, 3, 4... Откаты транзакций, параллельные запросы, кэширование — всё это создаёт пропуски. Если встретите "заказ №102 после №99" — это нормально.Если действительно нужна непрерывная нумерация (например, для счетов-фактур по требованиям бухгалтерии), SEQUENCE — не лучший выбор.
-
Забытый MAXVALUE — бомба замедленного действия 💥
Установили MAXVALUE 999999 и забыли? После миллионной записи всё встанет. Для production всегда используйте максимально возможное значение или мониторьте приближение к пределу.
Подводя итоги
Для большинства задач SERIAL справляется отлично. Но если вам нужна общая нумерация для нескольких таблиц, специфический шаг приращения или возможность получить ID до вставки — SEQUENCE решает эти задачи элегантно.
Главное — не забывайте про пропуски в нумерации и следите за MAXVALUE. В остальном это простой и мощный инструмент! 🚀