Postgres INSERT ON CONFLICT do UPDATE vs вставка или обновление
я stock_price_alert
таблица с 3 столбцами. stock_price_id
is PRIMARY KEY
и FOREIGN KEY
на другой стол. Определение таблицы, как показано ниже:
create table stock_price_alert (
stock_price_id integer references stock_price (id) on delete cascade not null,
fall_below_alert boolean not null,
rise_above_alert boolean not null,
primary key (stock_price_id)
);
мне нужно:
1) INSERT
запись, если не существует
-- query 1
INSERT INTO stock_price_alert (stock_price_id, fall_below_alert, rise_above_alert)
VALUES (1, true, false);
2) UPDATE
запись, если существует
-- query 2
UPDATE stock_price_alert SET
fall_below_alert = true,
rise_above_alert = false
WHERE stock_price_id = 1;
сначала мне нужно вопрос SELECT
запрос stock_price_alert
таблица, чтобы решить, выполнять ли запрос (1) или (2).
поддержка Postgres INSERT INTO TABLE .... ON CONFLICT DO UPDATE ...
:
-- query 3
INSERT INTO stock_price_alert (stock_price_id, fall_below_alert, rise_above_alert)
VALUES (1, true, false)
ON CONFLICT (stock_price_id) DO UPDATE SET
fall_below_alert = EXCLUDED.fall_below_alert,
rise_above_alert = EXCLUDED.rise_above_alert;
вместо использования запроса (1) или (2) Можно ли всегда использовать запрос (3)? Тогда мне не нужно выдавать SELECT
запрос в ДО & это помогает упростить код.
но мне интересно, какая лучшая практика? Вызовет ли запрос (3) проблему производительности или нежелательный побочный эффект? Спасибо.
2 ответов
запрос 3-это синтаксис Postgres для "UPSERT" (=UPDATE или INSERT), введенный в Postgres 9.5.
С документация:
ON CONFLICT DO UPDATE
гарантии атомнуюINSERT
илиUPDATE
результат; при условии, что нет независимой ошибки, один из этих двух результатов гарантировано, даже при высоком параллелизме. Это также известно какUPSERT
– "UPDATE
илиINSERT
".
это лучшая практика для того, что вы пытаюсь достичь.
Я заметил / протестировал, что намного быстрее для вставок (еще предстоит протестировать UPSERTS)использовать, где не существует в дополнение к конфликту. Обычно около 3x быстрее, чем просто позволяет конфликту ON обрабатывать проверки существования. Я думаю, что это может переноситься в UPSERTS, что делает его более быстрым для вставки, а затем и обновления. Вот мой тест только для вставок...
--so i can keep rerunning
DROP TABLE if exists temp1;
DROP TABLE if exists temp2;
--create a billion rows
SELECT GENERATE_SERIES AS id INTO TEMP temp1
FROM GENERATE_SERIES(1, 10000000);
CREATE UNIQUE INDEX ux_id ON temp1(id);
ALTER TABLE temp1 CLUSTER ON ux_id;
--create a second table to insert from, with the same data
SELECT * INTO TEMP temp2
FROM temp1;
CREATE UNIQUE INDEX ux_id2 ON temp2(id);
ALTER TABLE temp2 CLUSTER ON ux_id2;
--test inserting with on conflict only
INSERT INTO temp1(id)
SELECT id
FROM temp2 ON conflict DO nothing;
--execution time: 14.71s (1million rows)
--test inserting with not exists and on conflict
INSERT INTO temp1(id)
SELECT t2.id
FROM temp2 t2
WHERE NOT EXISTS (SELECT 1 FROM temp1 t1 WHERE t2.id = t1.id)
ON conflict DO nothing;
--execution time: 5.78s (1million rows)