postgres: обновить строку при вставке конфликта и вернуть старые значения - PullRequest
2 голосов
/ 16 марта 2019

Мне нужен запрос для обновления строки в таблице, но если идентификатор не существует, он вставляет значения по умолчанию.Кроме того, он должен избегать условий гонки потоков.

Я нашел здесь ответ, который должен быть в порядке https://stackoverflow.com/a/7927957/8372336

Используя этот запрос:

UPDATE tbl x
SET    tbl_id = 24
     , name = 'New Gal'
FROM  (SELECT tbl_id, name FROM tbl WHERE tbl_id = 4 FOR UPDATE) y 
WHERE  x.tbl_id = y.tbl_id
RETURNING y.tbl_id AS old_id, y.name AS old_name, x.tbl_id, x.name;

Так что я думаю, что этодолжен возвращать старые значения после обновления, и это должно предотвращать состязание потоков.

Но мне нужно добавить вставку, если строка не существует, а также вернуть вставленные значения на этот раз (старые значения не имеют смыслапотому что их не было).

Так что в основном мне нужно сделать что-то вроде

INSERT INTO tbl 
    (...) VALUES (...) 
    RETURNING ..., ... 
ON CONFLICT DO
UPDATE tbl x
SET    tbl_id = 24
     , name = 'New Gal'
FROM  (SELECT tbl_id, name FROM tbl WHERE tbl_id = 4 FOR UPDATE) y 
WHERE  x.tbl_id = y.tbl_id
RETURNING y.tbl_id AS old_id, y.name AS old_name, x.tbl_id, x.name;

, но я не уверен, что что-то подобное может сработать.как я могу заставить его работать и быть уверенным в условиях гонки?

1 Ответ

2 голосов
/ 16 марта 2019

Каким-то образом это работает:

insert into t (x)
    values ('a0'), ('b')
    on conflict (x) do update
        set x = excluded.x || '0'
    returning i, x, (select x from t t2 where t2.i = t.i);

Я удивлен, потому что t входит в область действия подзапроса, а excluded - нет.Ммм,,возможно, это связано с тем, что это не часть предложения on conflict, а часть общего insert.Это начинает иметь смысл.

Здесь - это скрипта db <> для этой версии.

Я думаю, ваш код будет выглядеть так:

INSERT INTO tbl (...)
    VALUES (...) 
ON CONFLICT DO
UPDATE tbl x
    SET tbl_id = 24,
        name = 'New Gal'
RETURNING (SELECT t2.tbl_id FROM tbl t2 WHERE t2.tbl_id = tbl.tbl_id) AS old_id, 
          (SELECT t2.name FROM tbl t2 WHERE t2.tbl_id = tbl.tbl_id) AS old_name, 
          x.tbl_id, x.name;
...