Функция PostgreSQL для последнего вставленного идентификатора

Как в PostgreSQL вставить последний идентификатор в таблицу?

В MS SQL есть SCOPE_IDENTITY ().

Пожалуйста, не советуйте мне использовать что-то вроде этого:

select max(id) from table

person Anton    schedule 31.05.2010    source источник
comment
почему вы ненавидите функцию max? Я думаю, это очень просто. Есть ли проблема с безопасностью?   -  person jeongmin.cha    schedule 25.11.2016
comment
@ jeongmin.cha есть проблема, если между ними есть другие операции и больше вставок (одновременных операций), означает, что максимальный идентификатор изменился, если и до тех пор, пока вы не возьмете блокировку явно и не освободите ее   -  person rohanagarwal    schedule 14.07.2017
comment
Если предполагаемый вариант использования заключается в использовании последнего вставленного идентификатора как части значения последующей вставки, см. этот вопрос.   -  person Flux    schedule 21.05.2019


Ответы (13)


(tl;dr: goto option 3: INSERT with RETURNING)

Напомним, что в postgresql отсутствует понятие «id» для таблиц, только последовательности (которые обычно, но не обязательно, используются в качестве значений по умолчанию для суррогатных первичных ключей с псевдотип SERIAL).

Если вы хотите получить идентификатор вновь вставленной строки, есть несколько способов:


Вариант 1: CURRVAL(<sequence name>);.

Например:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

Название последовательности должно быть известно, это действительно произвольно; в этом примере мы предполагаем, что таблица persons имеет столбец id, созданный с псевдотипом SERIAL. Чтобы не полагаться на это и чувствовать себя более чистым, вы можете вместо этого использовать pg_get_serial_sequence < / а>:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

Предупреждение: currval() работает только после INSERT (который выполнил nextval()), в том же сеансе.


Вариант 2: LASTVAL();

Это похоже на предыдущее, только вам не нужно указывать имя последовательности: он ищет самую последнюю измененную последовательность (всегда внутри вашего сеанса, такое же предостережение, как указано выше).


И CURRVAL, и LASTVAL полностью безопасны одновременно. Поведение последовательности в PG спроектировано так, что разные сеансы не будут мешать, поэтому нет риска состояния гонки (если другой сеанс вставляет другую строку между моим INSERT и моим SELECT, я все равно получаю свое правильное значение).

Однако у них действительно есть небольшая потенциальная проблема. Если в базе данных есть TRIGGER (или ПРАВИЛО), которое при вставке в persons, делает дополнительные вставки в другие таблицы ... тогда LASTVAL, вероятно, даст нам неправильное значение. Проблема может возникнуть даже с CURRVAL, если дополнительные вставки выполняются в той же persons таблице (это гораздо менее обычное дело, но риск все же существует).


Вариант 3: INSERT с RETURNING

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;

Это наиболее чистый, эффективный и безопасный способ получить идентификатор. У него нет никаких рисков предыдущего.

Недостатки? Почти нет: вам может потребоваться изменить способ вызова вашего оператора INSERT (в худшем случае, возможно, ваш уровень API или БД не ожидает, что INSERT вернет значение); это не стандартный SQL (кого это волнует); он доступен с Postgresql 8.2 (декабрь 2006 ...)


Вывод: если можете, выберите вариант 3. В другом месте предпочтите 1.

Примечание: все эти методы бесполезны, если вы намереваетесь получить последний вставленный идентификатор глобально (не обязательно во время вашего сеанса). Для этого вы должны прибегнуть к SELECT max(id) FROM table (конечно, это не будет читать незафиксированные вставки из других транзакций).

И наоборот, вы не должны никогда использовать SELECT max(id) FROM table вместо одного из трех приведенных выше вариантов, чтобы получить идентификатор, только что сгенерированный вашим оператором INSERT, потому что (помимо производительности) это небезопасно одновременно: между вашим INSERT и вашим SELECT другой сеанс мог вставить другую запись.

person leonbloy    schedule 31.05.2010
comment
LASTVAL () может быть очень опасным, если вы добавляете триггер / правило, вставляющее строки в другую таблицу. - person Kouber Saparev; 25.10.2012
comment
SELECT max(id), к сожалению, тоже не выполняет свою работу, как только вы начинаете удалять строки. - person Simon A. Eugster; 02.11.2012
comment
@leonbloy Если я что-то не пропустил, если у вас есть строки с идентификаторами 1,2,3,4,5 и вы удалите строки 4 и 5, последний вставленный идентификатор по-прежнему равен 5, но max() возвращает 3. - person Simon A. Eugster; 03.11.2012
comment
@ SimonA.Eugster А, нет, под последним вставленным идентификатором глобально я имел в виду среди существующих в настоящее время строк - это то, что обычно интересует, - хотя я согласен, что моя формулировка не очень хороша. - person leonbloy; 03.11.2012
comment
@leonbloy А, ладно, меня заинтересовал другой;) - person Simon A. Eugster; 04.11.2012
comment
Я использую CURRVAL в postgresql 8.2, но это дает мне ошибку Currval not supported. Любая работа по этому поводу очень ценится. Спасибо. - person Nemo; 03.04.2014
comment
Я только что узнал, что проблема с LASTVAL () не только в случае триггеров. Когда я вставляю что-то вроде insert into some_table (some_column) values (some_function()), тогда some_function(), по-видимому, выполняется позже, чем сама вставка, поэтому, если он также что-то вставляет, LASTVAL () вернет идентификатор этого. - person petersohn; 11.04.2014
comment
Я бы избегал использования SELECT MAX () в пользу SELECT last_value FROM person_id_seq. Намного быстрее. - person Gustavo Pinsard; 09.05.2014
comment
@GustavoPinsard: SELECT MAX () и SELECT last_value - разные вещи и могут давать разные результаты. - person leonbloy; 09.05.2014
comment
Приветствуется пример того, как использовать RETURNING id;, чтобы вставить это в другую таблицу! - person Olivier Pons; 28.12.2015
comment
На всякий случай, кому интересно - вот список того, как это возможно на других языках баз данных. Как следует из ответа (типа), нет прямого SQL-совместимого способа получить последний вставленный идентификатор. - person villapx; 16.03.2016
comment
Как я могу использовать RETURNING id в сценарии SQL, передаваемом в psql инструмент командной строки? - person amoe; 04.08.2016
comment
Вариант 1 Сработал для меня как шарм. +1 - person AVK; 26.09.2016
comment
LASTVAL () - это не зло, а триггеры - зло - person Davos; 13.11.2017
comment
Я знаю, что у вас есть tl; dr, но я не уверен, почему вы заказали свои варианты именно так, вам следует переместить вариант 3 в вариант 1, так как это наиболее практичное решение, которое в любом случае выберет большинство пользователей. - person Airerr; 22.07.2018
comment
вариант 3 - самый простой и удобный вариант. Благодарность - person Eswar; 29.05.2019
comment
Как сохранить последний идентификатор вставки в переменной, чтобы использовать его в следующий раз? - person logbasex; 20.01.2021
comment
Как сделать две вставки и использовать возвращаемый идентификатор первой вставки во второй вставке? - person ceving; 16.07.2021

См. Предложение RETURNING в инструкции INSERT. По сути, INSERT выполняет функцию запроса и возвращает значение, которое было вставлено.

person kwatford    schedule 31.05.2010
comment
Работает с версии 8.2 и является лучшим и самым быстрым решением. - person Frank Heikens; 31.05.2010
comment
может быть, быстрое объяснение того, как использовать указанный возвращенный идентификатор? - person Andrew; 12.04.2016
comment
@ Андрей Я не уверен, что понимаю вопрос. Вы не знаете, как получить результаты по запросу? Это зависит от языка / библиотеки и должно работать одинаково независимо от того, выполняете ли вы выборку или возвращаемую вставку. Единственная другая интерпретация, которую я могу придумать, - это то, что вы успешно получили идентификатор из вызова и не знаете, для чего он нужен ... в таком случае, зачем вы его извлекали? - person kwatford; 21.04.2016
comment
@Andrew, если вы запускаете из командной строки psql this: insert into names (firstname, lastname) values ('john', 'smith') returning id;, то он просто выводит id, как если бы вы запускали select id from names where id=$lastid напрямую. Если вы хотите сохранить возврат в переменную, тогда insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;. Если оператор, содержащий возвращаемый, является последним оператором в функции, тогда возвращается идентификатор, который был returninged функцией в целом. - person Alexander Bird; 16.07.2016

Ответ Леонблоя довольно полный. Я бы добавил только особый случай, когда нужно получить последнее вставленное значение из функции PL / pgSQL, где ВАРИАНТ 3 не подходит точно.

Например, если у нас есть следующие таблицы:

CREATE TABLE person(
   id serial,
   lastname character varying (50),
   firstname character varying (50),
   CONSTRAINT person_pk PRIMARY KEY (id)
);

CREATE TABLE client (
    id integer,
   CONSTRAINT client_pk PRIMARY KEY (id),
   CONSTRAINT fk_client_person FOREIGN KEY (id)
       REFERENCES person (id) MATCH SIMPLE
);

Если нам нужно вставить запись о клиенте, мы должны обратиться к записи о человеке. Но предположим, что мы хотим разработать функцию PL / pgSQL, которая вставляет новую запись в клиент, но также заботится о вставке новой записи о человеке. Для этого мы должны использовать небольшую вариацию ВАРИАНТА 3 leonbloy:

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];

Обратите внимание, что есть два предложения INTO. Следовательно, функция PL / pgSQL будет определяться следующим образом:

CREATE OR REPLACE FUNCTION new_client(lastn character varying, firstn character varying)
  RETURNS integer AS
$BODY$
DECLARE
   v_id integer;
BEGIN
   -- Inserts the new person record and retrieves the last inserted id
   INSERT INTO person(lastname, firstname)
   VALUES (lastn, firstn)
   RETURNING id INTO v_id;

   -- Inserts the new client and references the inserted person
   INSERT INTO client(id) VALUES (v_id);

   -- Return the new id so we can use it in a select clause or return the new id into the user application
    RETURN v_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

Теперь мы можем вставить новые данные, используя:

SELECT new_client('Smith', 'John');

or

SELECT * FROM new_client('Smith', 'John');

И получаем только что созданный id.

new_client
integer
----------
         1
person Krauss    schedule 19.08.2016
comment
Этот ответ невероятно полезен. Вы не найдете конкретных RETURNING id INTO [new_variable] примеров в официальных документах Postrgres. - person Chris Kobrzak; 29.09.2020

вы можете использовать предложение RETURNING в инструкции INSERT, как показано ниже

wgzhao=# create table foo(id int,name text);
CREATE TABLE
wgzhao=# insert into foo values(1,'wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into foo values(3,'wgzhao') returning id;
 id 
----
  3
(1 row)

INSERT 0 1

wgzhao=# create table bar(id serial,name text);
CREATE TABLE
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  2
(1 row)

INSERT 0 
person wgzhao    schedule 24.08.2012

См. Пример ниже

CREATE TABLE users (
    -- make the "id" column a primary key; this also creates
    -- a UNIQUE constraint and a b+-tree index on the column
    id    SERIAL PRIMARY KEY,
    name  TEXT,
    age   INT4
);

INSERT INTO users (name, age) VALUES ('Mozart', 20);

Затем для получения последнего вставленного идентификатора используйте это для таблицы "пользователь" имя столбца seq "id"

SELECT currval(pg_get_serial_sequence('users', 'id'));
person RINSON KE    schedule 31.05.2010

SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))

Конечно, вам нужно указать имя таблицы и имя столбца.

Это будет для текущего сеанса / соединения http://www.postgresql.org/docs/8.3/static/functions-sequence.html

person jishi    schedule 31.05.2010

Для тех, кому нужно получить всю запись данных, вы можете добавить

returning *

до конца вашего запроса, чтобы получить весь объект, включая идентификатор.

person emreoktem    schedule 05.01.2017

Попробуй это:

select nextval('my_seq_name');  // Returns next value

Если это возвращает 1 (или любое другое значение start_value для вашей последовательности), то сбросьте последовательность обратно к исходному значению, передав флаг false:

select setval('my_seq_name', 1, false);

Иначе,

select setval('my_seq_name', nextValue - 1, true);

Это восстановит значение последовательности в исходное состояние, и "setval" вернется со значением последовательности, которое вы ищете.

person Oozman    schedule 27.02.2014

Postgres имеет встроенный механизм для того же самого, который в том же запросе возвращает идентификатор или все, что вы хотите, чтобы запрос возвращал. вот пример. Предположим, у вас есть таблица с 2 столбцами column1 и column2, и вы хотите, чтобы column1 возвращался после каждой вставки.

# create table users_table(id serial not null primary key, name character varying);
CREATE TABLE
#insert into users_table(name) VALUES ('Jon Snow') RETURNING id;
 id 
----
  1
(1 row)

# insert into users_table(name) VALUES ('Arya Stark') RETURNING id;
 id 
----
  2
(1 row)
person Sandip Debnath    schedule 30.08.2018

Вы можете использовать RETURNING id после запроса вставки.

INSERT INTO distributors (id, name) VALUES (DEFAULT, 'ALI') RETURNING id;

и результат:

 id 
----
  1

В приведенном выше примере id - это поле с автоматическим приращением.

person Mohammad Fallah    schedule 08.11.2020

Другие ответы не показывают, как можно использовать значения, возвращаемые RETURNING. Вот пример, в котором возвращаемое значение вставляется в другую таблицу.

WITH inserted_id AS (
  INSERT INTO tbl1 (col1)
  VALUES ('foo') RETURNING id
)

INSERT INTO tbl2 (other_id) 
VALUES ((select id from inserted_id));
person snakecharmerb    schedule 13.06.2021

У меня была эта проблема с Java и Postgres. Я исправил это, обновив новую версию Connector-J.

postgresql-9.2-1002.jdbc4.jar

https://jdbc.postgresql.org/download.html: версия 42.2.12

https://jdbc.postgresql.org/download/postgresql-42.2.12.jar

person Cristian    schedule 30.04.2020

Основываясь на ответе @ooZman выше, похоже, что это работает для PostgreSQL v12, когда вам нужно ВСТАВИТЬ следующее значение последовательности (аналогично auto_increment), не обманывая ничего в счетчиках ваших таблиц. (Примечание: я не тестировал его в более сложных конфигурациях кластера БД ...)

Псевдо-код

$ insert_next_id = $ return_result- ›запрос (select (setval('"your_id_seq"', (select nextval('"your_id_seq"')) - 1, true)) +1;)

person K8sN0v1c3    schedule 16.12.2020