( tl;dr
:отидете на опция 3:INSERT с 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 или DB слой не очаква INSERT да върне стойност); това не е стандартен SQL (на кой му пука); наличен е от Postgresql 8.2 (декември 2006 г....)
Заключение:Ако можете, изберете вариант 3. На друго място предпочитайте 1.
Забележка:всички тези методи са безполезни, ако възнамерявате да получите последния вмъкнат идентификатор глобално (не е задължително от вашата сесия). За това трябва да прибягвате до SELECT max(id) FROM table
(разбира се, това няма да чете незаети вмъквания от други транзакции).
И обратното, не трябва никога използвайте SELECT max(id) FROM table
вместо една от 3-те опции по-горе, за да получите току-що генерирания идентификатор от вашия INSERT
изявление, защото (освен производителността) това не е едновременно безопасно:между вашия INSERT
и вашият SELECT
друга сесия може да е вмъкнала друг запис.