Чи можу я використовувати повернене значення INSERT… RETURNING в іншому INSERT?


86

Чи можливо щось подібне?

INSERT INTO Table2 (val)
VALUES ((INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id));

як використання поверненого значення як значення для вставки рядка у другу таблицю з посиланням на першу таблицю?

Відповіді:


103

Ви можете зробити це, починаючи з Postgres 9.1:

with rows as (
INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id
)
INSERT INTO Table2 (val)
SELECT id
FROM rows

Тим часом, якщо вас цікавить лише ідентифікатор, ви можете зробити це за допомогою тригера:

create function t1_ins_into_t2()
  returns trigger
as $$
begin
  insert into table2 (val) values (new.id);
  return new;
end;
$$ language plpgsql;

create trigger t1_ins_into_t2
  after insert on table1
for each row
execute procedure t1_ins_into_t2();

1
Як вставити значення біля ідентифікатора, що повертається? наприклад: ВСТАВИТИ В ТАБЛИЦЮ2 (val1, val2, val3) (1, 2, SELECT id FROM рядків)
Махмуд Ханафі

@MahmoudHanafy: заміна rowsна (some_query returning ...)може працювати сьогодні (не пробував).
Денис де Бернарді

2
@MahmoudHanafy: Щоб вставити значення поруч із ідентифікатором, що повертається, ви можете зробити щось подібне: ВСТАВИТИ В ТАБЛИЦЮ2 (val1, val2, val3) ВИБЕРІТЬ ідентифікатор, 1, 2 ВІД рядків
Бінді,

проголосував! чи це атомне значення, якщо перша вставка успішна, а друга - що не відбувається тоді?
PirateApp

2
@PirateApp Щойно перевірено! v12.4. Перший INSERT справді відкочується, якщо другий не вдається, але серія / автоінкремент першого INSERT пропускається
Madacol

57

Найкраща практика для цієї ситуації. Використовуйте RETURNING … INTO.

INSERT INTO teams VALUES (...) RETURNING id INTO last_id;

Зверніть увагу, це для PLPGSQL


3
Це насправді річ? Здається, жодна частина документа, до якого ви зв’язали, не згадує RETURNING ... INTO.
Алек


@PedroD: Це так.
Барт Хофланд,

13

Відповідно до відповіді Дениса де Бернарді ..

Якщо ви хочете, щоб ідентифікатор повертався згодом, і ви хочете вставити більше речей у Таблицю2:

with rows as (
INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id
)
INSERT INTO Table2 (val, val2, val3)
SELECT id, 'val2value', 'val3value'
FROM rows
RETURNING val

10
DO $$
DECLARE tableId integer;
BEGIN
  INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id INTO tableId;
  INSERT INTO Table2 (val) VALUES (tableId);
END $$;

Перевірено за допомогою psql (10.3, сервер 9.6.8)


8

Ви можете використовувати lastval()функцію:

Повернене значення, отримане за останнім часом nextvalдля будь-якої послідовності

Отож приблизно так:

INSERT INTO Table1 (name) VALUES ('a_title');
INSERT INTO Table2 (val)  VALUES (lastval());

Це буде працювати нормально, доки ніхто не звернеться nextval()до будь-якої іншої послідовності (у поточному сеансі) між вашими INSERT.

Як зазначив Денис нижче, і я попереджав про це вище, використання lastval()може призвести до проблем, якщо доступ до іншої послідовності відбувається nextval()між вашими INSERT. Це може статися , якщо був вставити тригер , Table1який вручну називається nextval()на послідовності або, що більш імовірно, зробив INSERT на таблиці з SERIALабоBIGSERIAL первинним ключем. Якщо ви хочете бути справді параноїком (добре, вони все-таки насправді це для вас), тоді ви можете скористатися цим, currval()але вам потрібно знати назву відповідної послідовності:

INSERT INTO Table1 (name) VALUES ('a_title');
INSERT INTO Table2 (val)  VALUES (currval('Table1_id_seq'::regclass));

Автоматично сформована послідовність зазвичай називається, t_c_seqде tє ім'я таблиці та cім'я стовпця, але ви завжди можете це дізнатись, зайшовши psqlта сказавши:

=> \d table_name;

а потім переглядаючи значення за замовчуванням для відповідного стовпця, наприклад:

id | integer | not null default nextval('people_id_seq'::regclass)

FYI: lastval()це, більш-менш, версія MySQL PostgreSQL LAST_INSERT_ID. Я згадую це лише тому, що багато людей більше знайомі з MySQL, ніж PostgreSQL, тому посилання lastval()на щось знайоме може пояснити речі.


2
Краще використовувати currval (), однак, якщо тригер на table1 робить подальші вставки.
Денис де Бернарді

@Denis: Правда, але тоді вам потрібно ім'я послідовності. Я додаю невелике оновлення з цього приводу лише для того, щоб охопити всі основи.
му занадто коротке

LASTVAL () та CURRVAL () обидва працюють для поточного з'єднання з базою даних, а не для інших з'єднань. Інші користувачі можуть оновити послідовність одночасно, що не змінить ваших результатів. Не турбуйтеся про інших, вони ніколи не змінять ваші результати для LASTVAL та / з CURRVAL. LASTVAL і CURRVAL взагалі не можна використовувати при використанні пулу з'єднань без транзакції, саме тоді все йде не так: Ви не контролюєте підключення до бази даних.
Френк Хейкенс

1
@Frank: Так, вони всі специфічні для сеансу, але проблема lastvalполягає в тому, що за вашою спиною може бути INSERT на основі послідовності від тригера AFTER INSERT на Таблиці1. Це буде в поточній сесії і, мабуть, зміниться, lastval()коли ви цього не очікуєте.
му занадто коротке

1

таблиця_ex

ідентифікатор за замовчуванням nextval ('table_id_seq' :: regclass),

табір1 варчар

табір2 варчар

INSERT INTO table_ex(camp1,camp2) VALUES ('xxx','123') RETURNING id 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.