(tl;dr
: перейти к варианту 3: ВСТАВИТЬ с ВОЗВРАТОМ)
Напомним, что в postgresql отсутствует концепция «id» для таблиц, просто sequence (которые обычно, но не обязательно, используются в качестве значений по умолчанию для суррогатных первичных ключей, с 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
(конечно, это не будет читать незафиксированные вставки из других транзакций).