J'ai stock_price_alert
table avec 3 colonnes. stock_price_id
est PRIMARY KEY
& aussi FOREIGN KEY
à une autre table. Définition du tableau ci-dessous:
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)
);
Je dois soit:
1) enregistrement INSERT
s'il n'existe pas
-- query 1
INSERT INTO stock_price_alert (stock_price_id, fall_below_alert, rise_above_alert)
VALUES (1, true, false);
2) enregistrement UPDATE
s'il existe
-- query 2
UPDATE stock_price_alert SET
fall_below_alert = true,
rise_above_alert = false
WHERE stock_price_id = 1;
Je dois d’abord émettre une requête SELECT
sur la table stock_price_alert
, afin de décider d’effectuer une requête (1) ou (2).
Postgres prend en charge 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;
Au lieu d'utiliser query (1) ou (2), puis-je toujours utiliser query (3)? Dans ce cas, je n'ai pas besoin d'émettre la requête SELECT
dans previous & cela aide à simplifier le code.
Mais je me demande quelle est la meilleure pratique? Query (3) causera-t-il un problème de performance ou des effets secondaires indésirables? Merci.
La requête 3 est la syntaxe Postgres pour "UPSERT" (= UPDATE ou INSERT), introduite dans Postgres 9.5.
De la documentation :
ON CONFLICT DO UPDATE
garantit un résultat atomiqueINSERT
ouUPDATE
; à condition qu'il n'y ait pas d'erreur indépendante, l'un de ces deux résultats est garanti, même sous forte concurrence. Ceci est également appeléUPSERT
- “UPDATE
ouINSERT
”.
C’est la meilleure pratique pour ce que vous essayez d’atteindre.
J'ai remarqué/testé qu'il est beaucoup plus rapide pour les INSERTS (qui n'ont pas encore testé UPSERTS) d'utiliser un WHERE NOT EXISTS en plus de ON CONFLICT. Généralement, il est environ 3 fois plus rapide que d’autoriser ON CONFLICT à gérer les vérifications d’existence. Je pense que cela pourrait se prolonger dans UPSERTS, ce qui rendrait probablement plus rapide la réalisation d'une insertion, puis d'une mise à jour. Voici mon test pour les inserts uniquement ...
--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)