обновить таблицу из результата вставки - PullRequest
0 голосов
/ 21 сентября 2018

Для таблицы B существует столбец с именем a_id, который является идентификатором таблицы A. Таким образом, a_id - это внешний ключ, указывающий на таблицу a, но это просто целочисленный столбец, для которого не установлено никаких внешних ограничений..

Для каждой строки в таблице B нам нужно дать столбцу a_id целочисленное значение, создав новую запись в таблице A.

Цель - выполнить все приведенные ниже шаги в одном SQL.

  1. Вставить все данные в таблицу A:

    insert into table A (name) values ('abc'), ('def'), ... returning id

  2. Buck update a_id каждой строки в таблице Bс идентификатором (каждый идентификатор должен использоваться только один раз), возвращаемым с шага 1

    update table B set a_id = id(from previous insert statement)

Пробовали что-то вроде:

обновить таблицу B, установить a_id = (выбрать ia.id из ia
(вставить в таблицу A (имя) значения ('abc'), ('def'), ... возвращая id) как ia)

Но это дает синтаксическую ошибку ERROR: syntax error at or near "into".

Как это сделать с одним SQL?

Ответы [ 2 ]

0 голосов
/ 02 июля 2019

Это не совсем один запрос, но если ваша задача на самом деле просто избежать расовых условий / аномалий изоляции транзакций, возникающих из-за наивного подхода сделать это с несколькими запросами, то это должно сделать это:

-- assign tableA's future primary keys into a_id without 
-- creating entries on tableA, pg_get_serial_sequence()
-- should retrieve tableA's primary key generator
UPDATE tableB
    SET a_id = nextval(pg_get_serial_sequence('tableA', 'id'))
    WHERE a_id IS NULL;

-- insert into tableB, with explicitly specified primary keys, 
-- note that this doesn't increment tableA's sequence as that 
-- has already been done in the previous operation by nextval()
INSERT INTO tableA(id, name)
    SELECT a_id, name FROM tableB;

Обратите внимание, что вы все равно должны заключить это в транзакцию, чтобы гарантировать, что обновления tableB будут откатываться в случае сбоя вставки по какой-либо причине.В качестве альтернативы, поскольку две вышеописанные операции являются идемпотентными, их можно безопасно повторить одновременно даже без транзакции.

0 голосов
/ 21 сентября 2018

Если вы вставляете только одну строку, вы можете использовать данные, модифицирующие CTE

with new_row as (
  insert into table_A (name) values ('abc')
  returning id
)
update table_b
  set a_id = (select id from new_row)
where ?????; -- surely you don't want to update all rows in table_b

Однако вышеприведенный завершится ошибкой , если ваша вставка более одногострока в первом утверждении.

Мне неясно, какие строки в table_b необходимо обновить в этом случае.

...