Uma tabela de amostra e dados
CREATE TABLE dupes(col1 int primary key, col2 int, col3 text,
CONSTRAINT col2_unique UNIQUE (col2)
);
INSERT INTO dupes values(1,1,'a'),(2,2,'b');
Reproduzindo o problema
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1) DO UPDATE SET col3 = 'c', col2 = 2
Vamos chamar isso de Q1. O resultado é
ERROR: duplicate key value violates unique constraint "col2_unique"
DETAIL: Key (col2)=(2) already exists.
conflito_target pode realizar inferência de índice exclusivo. Ao realizar inferência, ele consiste em uma ou mais colunas index_column_name e / ou expressões index_expression e um index_predicate opcional. Todos os índices exclusivos nome_da_tabela que, independentemente da ordem, contêm exatamente as colunas / expressões especificadas por conflito_alvo são inferidos (escolhidos) como índices de árbitro. Se um index_predicate for especificado, ele deve, como um requisito adicional para inferência, satisfazer os índices do árbitro.
Isso dá a impressão de que a consulta a seguir deve funcionar, mas não funciona porque, na verdade, seria necessário um índice exclusivo em col1 e col2. No entanto, tal índice não garantiria que col1 e col2 seriam únicas individualmente, o que é um dos requisitos do OP.
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2
Vamos chamar essa consulta de Q2 (falha com um erro de sintaxe)
Por quê?
O Postgresql se comporta dessa forma porque o que deveria acontecer quando ocorre um conflito na segunda coluna não está bem definido. Existem várias possibilidades. Por exemplo, na consulta Q1 acima, o postgresql deve ser atualizado col1
quando há um conflito col2
? Mas e se isso levar a outro conflito col1
? como o postgresql deve lidar com isso?
Uma solução
Uma solução é combinar ON CONFLICT com UPSERT antiquado .
CREATE OR REPLACE FUNCTION merge_db(key1 INT, key2 INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
UPDATE dupes SET col3 = data WHERE col1 = key1 and col2 = key2;
IF found THEN
RETURN;
END IF;
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col1) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col2) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
END;
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
Você precisaria modificar a lógica desta função armazenada para que ela atualize as colunas exatamente da maneira que você deseja. Invoque como
SELECT merge_db(3,2,'c');
SELECT merge_db(1,2,'d');