Como UPSERT (MERGE, INSERT… ON DUPLICATE UPDATE) no PostgreSQL?

Uma pergunta muito freqüente aqui é como fazer um upsert, que é o que o MySQL chamaINSERT ... ON DUPLICATE UPDATE e os suportes padrão como parte doMERGE Operação.

Dado que o PostgreSQL não suporta diretamente (antes da página 9.5), como você faz isso? Considere o seguinte:

CREATE TABLE testtable (
    id integer PRIMARY KEY,
    somedata text NOT NULL
);

INSERT INTO testtable (id, somedata) VALUES
(1, 'fred'),
(2, 'bob');

Agora imagine que você quer "insistir" nas tuplas(2, 'Joe'), (3, 'Alan'), então o novo conteúdo da tabela seria:

(1, 'fred'),
(2, 'Joe'),    -- Changed value of existing tuple
(3, 'Alan')    -- Added new tuple

É disso que as pessoas estão falando quando discutemupsert. Crucialmente, qualquer abordagem deve serseguro na presença de várias transações trabalhando na mesma mesa - usando bloqueio explícito ou defendendo contra as condições de corrida resultantes.

Este tópico é discutido extensivamente emInserir, em atualização duplicada no PostgreSQL?, mas trata-se de alternativas à sintaxe do MySQL, e cresceu um pouco de detalhes não relacionados ao longo do tempo. Estou trabalhando em respostas definitivas.

Estas técnicas também são úteis para "insert if not exists, caso contrário não faz nada", isto é, "insert ... on key duplicate ignore".

questionAnswers(6)

yourAnswerToTheQuestion