# Функции nextval и currval

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

## Функция nextval

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

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

```sql
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)
);
```

{% hint style="warning" %}
Во избежание блокирования параллельных транзакций, пытающихся получить значения одной последовательности, операция `nextval` никогда не откатывается; то есть, как только значение было выбрано, оно считается использованным и не будет возвращено снова. Это утверждение верно, даже когда окружающая транзакция впоследствии прерывается или вызывающий запрос никак не использует это значение.
{% endhint %}

{% hint style="info" %}
Если в качестве типа колонки используется *serial* или *bigserial*, то такие типы автоматически создают последовательности и неявным образом вызывают `nextval,` если при добавлении записи в таблицу полю не задали конкретное значение.
{% endhint %}

## Функция currval

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

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

```xml
<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-запрос:

```sql
SELECT last_value FROM user_id_seq;
```

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