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


321

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

В MS SQL есть SCOPE_IDENTITY ().

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

select max(id) from table

почему ты ненавидишь функцию max? Я думаю, что это очень просто. Есть ли такая проблема, как безопасность?
jeongmin.cha

9
@ jeongmin.cha есть проблема, если между ними есть другие операции и больше вставок (параллельные операции), означает, что максимальный идентификатор изменился, если только и до тех пор, пока вы явно не берете блокировку и не
снимаете

Если предполагаемый вариант использования заключается в использовании последнего вставленного идентификатора как части значения последующей вставки, см. Этот вопрос .
поток

Ответы:


606

( tl;dr: переход к варианту 3: вставка с возвратом)

Напомним, что в 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 (или RULE), который при вставке в personsтаблицу делает дополнительные вставки в другие таблицы ... тогда LASTVAL, вероятно, даст нам неправильное значение. Проблема может даже случиться CURRVAL, если дополнительные вставки выполняются в одну и ту же personsтаблицу (это гораздо реже, но риск все еще существует).


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

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

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

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


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

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

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


25
LASTVAL () может быть очень злым, если вы добавляете триггер / правило, вставляющее строки в себя в другую таблицу.
Кубер Сапарев

3
SELECT max(id)к сожалению, не выполняет работу, как только вы начинаете удалять строки.
Саймон Эугстер

2
@leonbloy Если я что-то пропустил, если у вас есть строки с идентификаторами 1,2,3,4,5и вы удалите строки 4 и 5, последний вставленный идентификатор по-прежнему 5, но max()возвращает 3.
Саймон А. Эугстер,

9
Пример того, как использовать это RETURNING id;для вставки в другую таблицу, будет приветствоваться!
Оливье Понс,

8
Как я могу использовать RETURNING idв сценарии SQL, поданного в psqlинструмент командной строки?
amoe

82

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


4
Работает с версии 8.2 и является лучшим и самым быстрым решением.
Фрэнк Хейкенс

9
может быть быстрое объяснение того, как использовать указанный возвращенный идентификатор?
Андрей

@ Андрей, я не уверен, что понимаю вопрос. Разве вы не знаете, как получить результаты запроса? Это зависит от языка / библиотеки и должно работать одинаково независимо от того, выполняете ли вы выбор или возврат. Единственное другое объяснение, которое я могу придумать, - это то, что вы успешно извлекли идентификатор из звонка и не знаете, для чего он ... в каком случае, почему вы его извлекали?
Кватфорд

8
@Andrew, если вы запускаете из командной строки psql this:, insert into names (firstname, lastname) values ('john', 'smith') returning id;то он просто выводит id так же, как если бы вы работали select id from names where id=$lastidнапрямую. Если вы хотите сохранить возврат в переменную aa , то insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;если оператор, содержащий возвращение, является последним оператором в функции , то редактируемый идентификатор returningвозвращается функцией в целом.
Александр Берд

32

Вы можете использовать предложение 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 

28

Ответ Леонблоя довольно полный. Я бы добавил только тот особый случай, когда нужно получить последнее вставленное значение из функции 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');

или

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

И мы получаем недавно созданный идентификатор.

new_client
integer
----------
         1

9

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

returning *

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


8

Смотрите пример ниже

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

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

SELECT currval(pg_get_serial_sequence('users', 'id'));


1

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

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» вернет значение последовательности, которое вы ищете.


1

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)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.