Функции nextval и currval

В этой статье рассматриваются функции для работы с последовательностями в рамках одной транзакции, когда в первом запросе добавляется запись в таблицу в базе данных, а в последующих запросах используется ее идентификатор.

Функция nextval

Функция nextval продвигает последовательность к следующему значению и возвращает его. Это атомарная операция: если nextval вызывается одновременно в нескольких сеансах, в результате каждого вызова будут гарантированно получены разные значения.

Как правило вызов этой функции указывается в качестве дефолтного значения для первичного ключа. Например, таблица пользователей:

CREATE TABLE public.user
(
  user_id smallint NOT NULL DEFAULT nextval('user_id_seq'::regclass),
  user_name character varying NOT NULL,
  user_full_name character varying NOT NULL,
  CONSTRAINT pk_user_id PRIMARY KEY (user_id),
  CONSTRAINT unique_by_user_name UNIQUE (user_name)
);

Во избежание блокирования параллельных транзакций, пытающихся получить значения одной последовательности, операция nextval никогда не откатывается; то есть, как только значение было выбрано, оно считается использованным и не будет возвращено снова. Это утверждение верно, даже когда окружающая транзакция впоследствии прерывается или вызывающий запрос никак не использует это значение.

Если в качестве типа колонки используется serial или bigserial, то такие типы автоматически создают последовательности и неявным образом вызывают nextval, если при добавлении записи в таблицу полю не задали конкретное значение.

Функция currval

Возвращает значение, выданное при последнем вызове nextval для этой последовательности в текущем сеансе. Если в данном сеансе nextval ни разу не вызывалась (явно или неявно) для данной последовательности, возвращается ошибка. Так как это значение ограничено рамками сеанса, эта функция выдаёт предсказуемый результат вне зависимости от того, вызвалась ли впоследствии nextval в других сеансах или нет.

Рассмотрим использования currval на примере добавления нового пользователя:

<SqlQuery Name="UserInsertSqlQuery">
  <Text>
    INSERT INTO public.user(user_name, user_full_name)
    VALUES({Name}, {FullName});
    
    INSERT INTO template.user(public_user_id)
    VALUES(currval('public.user_id_seq'))
    RETURNING user_id;
  </Text>
</SqlQuery>

Так как nextvalуказан в качестве дефолтного значения для колонки user_id таблицы public.user и в запросе не указывается конкретное значение, то при выполнении команды INSERT она сработает автоматически, что позволяет использовать currval в следующей команде INSERT.

Значение last_value

Последовательность хранит свое значение во внутренней таблице. Получить это значение можно через last_value и SELECT-запрос:

SELECT last_value FROM user_id_seq;

Преимущество такого способа в том, что для получения значения не нужно вызывать nextval в том же сеансе. Но значение last_value не является потокобезопасным. Если last_value будет вызываться в нескольких сеансах одновременно, то во всех сеансах будет использоваться одно и тоже значение, что может нарушить целостность данных.

Last updated