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)