Могу ли я использовать возвращаемое значение 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
Как вставить значения рядом с возвращаемым идентификатором? например: INSERT INTO TABLE2 (val1, val2, val3) (1, 2, SELECT id FROM rows)
Махмуд Ханафи
@MahmoudHanafy: в настоящее время может работать замена rowsна (some_query returning ...)(не пробовал).
Дени де Бернарди
2
@MahmoudHanafy: чтобы вставить значения рядом с возвращаемым идентификатором, вы можете сделать что-то вроде этого: INSERT INTO TABLE2 (val1, val2, val3) SELECT id, 1, 2 FROM rows
Bhindi
проголосовали! это атомарный смысл, если первая вставка успешна, а вторая нет, что происходит потом?
PirateApp
2
@PirateApp Только что протестировано! v12.4. Первый INSERT действительно откатывается, если второй не удается, но серия / автоинкремент первого INSERT пропускается
Madacol
57

Лучшая практика для этой ситуации. Используйте RETURNING … INTO.

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

Обратите внимание, это для PLPGSQL

Александр Асси
источник
3
Это на самом деле вещь? Кажется, что ни одна часть документа, на который вы ссылаетесь, не упоминается RETURNING ... INTO.
Alec
4
@Alec: Я нашел эту документацию в этом ответе .
Барт Хофланд
@PedroD: Это так.
Барт Хофланд
13

В соответствии с ответом Дени де Бернарди ..

Если вы хотите, чтобы id также возвращался позже, и хотите вставить больше вещей в Table2:

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. Это могло произойти, если бы был триггер 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)

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

mu слишком короткий
источник
2
Однако лучше использовать currval (), если триггер на table1 выполняет последующие вставки.
Дени де Бернарди
@ Денис: Верно, но тогда вам нужно название последовательности. Я добавлю небольшое обновление к этому эффекту, чтобы охватить все основы.
mu слишком короткий
LASTVAL () и CURRVAL () работают для текущего соединения с базой данных, а не для других соединений. Другие пользователи могут обновить последовательность одновременно, это не повлияет на ваши результаты. Не беспокойтесь о других, они никогда не изменят ваши результаты для LASTVAL и / CURRVAL. LASTVAL и CURRVAL вообще не могут использоваться при использовании пула соединений без ТРАНЗАКЦИИ, вот когда что-то идет не так: вы не контролируете соединение с базой данных.
Frank Heikens
1
@Frank: Да, все они зависят от сеанса, но проблема в lastvalтом, что за вашей спиной может быть INSERT на основе последовательности из триггера AFTER INSERT в Table1. Это будет в текущем сеансе и, по-видимому, изменится, lastval()когда вы этого не ожидаете.
mu слишком короткий
1

table_ex

id по умолчанию nextval ('table_id_seq' :: regclass),

camp1 varchar

camp2 varchar

INSERT INTO table_ex(camp1,camp2) VALUES ('xxx','123') RETURNING id 
kemado77
источник