Як я можу використовувати currval () у PostgreSQL, щоб отримати останній вставлений ідентифікатор?


64

У мене є таблиця:

CREATE TABLE names (id serial, name varchar(20))

Я хочу "останній вставлений ідентифікатор" з цієї таблиці, не використовуючи RETURNING idвставку. Здається, є функція CURRVAL(), але я не розумію, як її використовувати.

Я спробував:

SELECT CURRVAL() AS id FROM names_id_seq
SELECT CURRVAL('names_id_seq')
SELECT CURRVAL('names_id_seq'::regclass)

але жоден з них не працює. Як я можу currval()отримати останній вставлений ідентифікатор?


2
Читачі цієї проблеми / рішення повинні усвідомлювати, що використання currval (), як правило, не відштовхується, оскільки пункт ПОВЕРНЕННЯ забезпечує ідентифікатор без накладних додаткових запитів і без можливості повернення значення WRONG VALUE (що currval зробить у деякі випадки використання.)
chander

1
@chander: чи є у вас посилання на цю претензію? Застосування currval() , безумовно, не відлякує.
a_horse_with_no_name

Можливо, це питання думки про те, чи не обмежує використання currval, але в деяких випадках користувачі повинні усвідомлювати, що це може призвести до значення, яке не є те, що ви очікуєте (таким чином зробивши ПОВЕРНЕННЯ кращим вибором там, де підтримується.) Припустимо, вам мати таблицю A, яка використовує послідовність a_seq, і таблицю B, яка також використовує a_seq (викликає nextval ('a_seq') для стовпця PK.) Припустимо, у вас також є тригер (a_trg), який вставляється в таблицю B ON INSERT в таблицю A. In у такому випадку функція currval () (після вставки в таблицю A) поверне число, сформоване для вставки в таблиці B, а не таблиці A.
chander

Відповіді:


51

Якщо ви створюєте стовпчик як serialPostgreSQL, автоматично створюється послідовність для цього.

Ім'я послідовності автоматично генерується і завжди є ім'ям_колунки_seq, у вашому випадку послідовністю будуть імена names_id_seq.

Після вставлення в таблицю ви можете зателефонувати currval()з цією назвою послідовності:

postgres=> CREATE TABLE names in schema_name (id serial, name varchar(20));
CREATE TABLE
postgres=> insert into names (name) values ('Arthur Dent');
INSERT 0 1
postgres=> select currval('names_id_seq');
 currval
---------
       1
(1 row)
postgres=>

Замість жорсткого кодування імені послідовності ви також можете використовувати pg_get_serial_sequence():

select currval(pg_get_serial_sequence('names', 'id'));

Таким чином, вам не потрібно покладатися на стратегію іменування, яку використовує Postgres.

Або якщо ви взагалі не хочете використовувати ім'я послідовності, використовуйте lastval()


Я думаю, що це не добре використовувати currval()в налаштуваннях для багатьох користувачів. Наприклад, на веб-сервері.
Йонас

9
Ні, ви не помиляєтесь. currval()є "локальним" для вашого поточного з'єднання. Тож немає жодної проблеми з його використанням у багатокористувацькому середовищі. Ось і вся мета послідовності.
a_horse_with_no_name

1
Це може зламатись у (досить рідкісному) випадку, коли ваша вставка запускає більше вставок у тій самій таблиці, ні?
leonbloy

1
@a_horse_with_no_name: Я думав не про декілька вставок (це легко помітити), а на (можливо, невідомі) тригери, визначені на столі. Спробуйте, наприклад, це вище: gist.github.com/anonymous/9784814
leonbloy

1
ЗАВЖДИ не ім’я таблиці та стовпців. Якщо вже є послідовність з цим ім'ям, вона створить нову послідовність шляхом об'єднання чи збільшення числа на кінці, і, можливо, знадобиться скоротити ім'я, якщо воно перевищує межу (яка, як видається, становить 62 символи).
PhilHibbs

47

Це прямо з Stack Overflow

Як вказували @a_horse_with_no_name та @Jack Douglas, currval працює лише з поточним сеансом. Тож якщо ви все гаразд з тим, що на результат може вплинути незапущена транзакція іншого сеансу, і ви все ще хочете, щоб щось працювало протягом сеансів, ви можете використовувати це:

SELECT last_value FROM your_sequence_name;

Для отримання додаткової інформації скористайтеся посиланням на SO.

З документації Postgres це чітко сказано

Помилково викликати lastval, якщо nextval ще не був викликаний у поточному сеансі.

Тож я думаю, строго кажучи, щоб правильно використовувати currval або last_value для послідовності протягом сеансів, вам потрібно зробити щось подібне?

SELECT setval('serial_id_seq',nextval('serial_id_seq')-1);

Припустимо, звичайно, що у вас не буде вставки або будь-якого іншого способу використання послідовного поля в поточному сеансі.


3
Я не можу придумати ситуацію, коли це було б корисно.
ypercubeᵀᴹ

Мені було просто цікаво, чи це спосіб отримати курраву, якщо nextval не був викликаний у поточній сесії. Будь-які пропозиції тоді?
Слак

Мені довелося це зробити, коли я чітко кодував первинні ключі для згенерованих даних кріплення, де я хотів, щоб значення ПК, які зазвичай генерувались, визначалися достроково, щоб полегшити тестування клієнтів. Щоб підтримувати вставки, виконуючи це в стовпці, який зазвичай регулюється значенням за замовчуванням від a nextval(), тоді вам доведеться вручну встановити послідовність, щоб відповідати кількості записів кріплення, які ви вставили з твердо кодованими ідентифікаторами. Крім того, спосіб вирішити проблему currval () / lastval (), недоступний перед наступним, - це просто SELECTна послідовності.
Пітер М. Еліас

@ ypercubeᵀᴹ Навпаки, я не можу думати, що немає вагомих причин використовувати "правильну" відповідь, яка була обрана. Ця відповідь не вимагає вставлення запису в таблицю. Це також відповідає на питання. Знову ж таки, я не можу подумати, що немає вагомих причин НЕ використовувати цю відповідь над обраним.
Henley Chiu

1
Ця відповідь взагалі не передбачає зміни таблиці. Перевірений. В ідеалі ви просто хочете знати останній ідентифікатор, не вносячи жодних змін. Що робити, якщо це виробнича БД? Ви не можете просто вставити випадковий рядок без можливих перкусій. Таким чином, ця відповідь є більш безпечною та правильною.
Henley Chiu

14

Вам потрібно зателефонувати nextvalза цією послідовністю на цьому сеансі ранішеcurrval :

create sequence serial;
select nextval('serial');
 nextval
---------
       1
(1 row)

select currval('serial');
 currval
---------
       1
(1 row)

тому ви не можете знайти "останній вставлений ідентифікатор" з послідовності, якщо це не insertбуде зроблено в тому ж сеансі (транзакція може відмовитися, але послідовність не буде)

як зазначено у відповіді a_horse, create tableстовпець типу serialавтоматично створить послідовність і використовуватиме її для генерування значень за замовчуванням для стовпця, тож insertнормально звертається nextvalнеявно:

create table my_table(id serial);
NOTICE:  CREATE TABLE will create implicit sequence "my_table_id_seq" for 
         serial column "my_table.id"

\d my_table
                          Table "stack.my_table"
 Column |  Type   |                       Modifiers
--------+---------+-------------------------------------------------------
 id     | integer | not null default nextval('my_table_id_seq'::regclass)

insert into my_table default values;
select currval('my_table_id_seq');
 currval
---------
       1
(1 row)

3

Отже, з цими різними методами є деякі проблеми:

Currval отримує лише останнє значення, сформоване в поточному сеансі - що чудово, якщо у вас немає нічого іншого, що генерує значення, але у випадках, коли ви можете викликати тригер та / або покращити послідовність не раз у поточній транзакції, це не збирається повертати правильне значення. Це не проблема для 99% людей там - але це щось, що варто враховувати.

Найкращий спосіб отримати унікальний ідентифікатор, присвоєний після операції вставки, - за допомогою пункту RETURNING. Приклад нижче передбачає, що стовпець, прив'язаний до послідовності, називається "id":

insert into table A (cola,colb,colc) values ('val1','val2','val3') returning id;

Зауважте, що корисність пункту ПОВЕРНЕННЯ виходить за рамки простого отримання послідовності, оскільки це також:

  • Повернені значення, які були використані для "остаточної вставки" (після, наприклад, ДО ПЕРЕДУМНОГО тригера, можливо, змінили дані, що вставляються.)
  • Поверніть значення, які видаляли:

    видалити з таблиці A, де id> 100 повертається *

  • Повернути змінені рядки після ОНОВЛЕННЯ:

    таблиця оновлення Набір X = 'y', де blah = 'blech' повертається *

  • Використовуйте результат видалення для оновлення:

    З A as (видалити * з таблиці A як ідентифікатор, що повертається) оновлення B set delete = true, де id in (виберіть id з A);


Звичайно, в ОП прямо сказали, що не хочуть використовувати RETURNINGпункт, але нічого поганого в тому, щоб зробити переваги використання цього зрозумілішим для інших.
RDFozz

Я справді просто намагався вказати на підводні камені різних інших методів (в тому випадку, якщо один з них не буде обережним, він може повернути значення, відмінне від очікуваного значення) - і уточнити кращу практику.
чандер

1

Мені довелося виконати запит, незважаючи на використання SQLALchemy, оскільки я не мав успіху у використанні currval.

nextId = db.session.execute("select last_value from <table>_seq").fetchone()[0] + 1

Це був проект колби python + postgresql.



1

Вам потрібно GRANTвикористовувати схему, наприклад:

GRANT USAGE ON SCHEMA schema_name to user;

і

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;

1
Ласкаво просимо на dba.se! Привіт за ваше перше повідомлення! Схоже, що Оригінальна публікація не стосується дозволів. Можливо, ви подумаєте розширити свою відповідь, щоб включити деякі специфіки щодо відмов у дозволах під час виклику currval()функції, щоб зробити її трохи більш відповідною до цього потоку?
Пітер Вандів'є

0

У PostgreSQL 11.2 ви можете обробляти послідовність як таблицю, схоже:

Приклад, якщо у вас є послідовність з ім'ям: 'names_id_seq'

select * from names_id_seq;
 last_value | log_cnt | is_called
------------+---------+-----------
          4 |      32 | t
(1 row)

Це повинно дати вам останній вставлений ідентифікатор (4 у цьому випадку), що означає, що поточне значення (або значення, яке слід використовувати для наступного ідентифікатора) має бути 5.


-2

Різні версії PostgreSQL можуть мати різні функції для отримання поточного або наступного ідентифікатора послідовності.

По-перше, ви повинні знати версію своїх Postgres. Використання вибору версії (); щоб отримати версію.

У PostgreSQL 8.2.15 ви отримуєте поточний ідентифікатор послідовності за допомогою select last_value from schemaName.sequence_name.

Якщо вищезазначене твердження не працює, ви можете використовувати select currval('schemaName.sequence_name');


2
Будь-який доказ для різних версій, що роблять це по-різному?
dezso
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.