Personnellement, j'ai mis en place une "règle" attachée à l'instruction d'insertion. Supposons que vous ayez une table "dns" qui enregistre les hits dns par client sur une base temporelle :
CREATE TABLE dns (
"time" timestamp without time zone NOT NULL,
customer_id integer NOT NULL,
hits integer
);
Vous vouliez être en mesure de réinsérer des lignes avec des valeurs mises à jour, ou de les créer si elles n'existaient pas déjà. Avec comme clé l'identifiant du client et l'heure. Quelque chose comme ça :
CREATE RULE replace_dns AS
ON INSERT TO dns
WHERE (EXISTS (SELECT 1 FROM dns WHERE ((dns."time" = new."time")
AND (dns.customer_id = new.customer_id))))
DO INSTEAD UPDATE dns
SET hits = new.hits
WHERE ((dns."time" = new."time") AND (dns.customer_id = new.customer_id));
Mise à jour : cette opération risque d'échouer si des insertions simultanées ont lieu, car elle générera des exceptions de type unique_violation. Toutefois, la transaction non terminée se poursuivra et réussira, et il vous suffira de répéter la transaction terminée.
Cependant, si des tonnes d'insertions se produisent en permanence, vous voudrez mettre un verrou de table autour des instructions d'insertion : Le verrouillage SHARE ROW EXCLUSIVE empêchera toute opération susceptible d'insérer, de supprimer ou de mettre à jour des lignes dans votre table cible. Cependant, les mises à jour qui ne mettent pas à jour la clé unique sont sûres, donc si vous ne prévoyez aucune opération de ce type, utilisez plutôt des verrous consultatifs.
De plus, la commande COPY n'utilise pas de RULES, donc si vous insérez avec COPY, vous devrez utiliser des triggers à la place.
46 votes
Quiconque trouve cette question devrait lire l'article de Depesz "Pourquoi l'upsert est si compliqué ?" . Il explique très bien le problème et les solutions possibles.
8 votes
UPSERT sera ajouté dans Postgres 9.5 : wiki.postgresql.org/wiki/
5 votes
@tommed - cela a été fait : stackoverflow.com/a/34639631/4418