Todos os produtos
Search
Central de documentação

Hologres:INSERT ON CONFLICT (UPSERT)

Última atualização: Jun 28, 2026

A instrução INSERT ON CONFLICT oferece semântica atômica de upsert: insere uma linha se não houver conflito ou atualiza (ou ignora) a linha existente caso detecte uma chave primária duplicada.

Use esta instrução ao importar dados diretamente com SQL. Para gravações via Data Integration e Flink, consulte Casos de uso.

Conceitos principais

A instrução suporta três modos de resolução de conflitos:

Modo

Cláusula SQL

Comportamento em caso de chave primária duplicada

InsertOrIgnore

DO NOTHING

Descarta a linha recebida; a linha existente permanece inalterada

InsertOrUpdate

DO UPDATE SET <columns>

Atualiza apenas as colunas listadas; as demais mantêm seus valores atuais

InsertOrReplace

DO UPDATE SET <all columns> com null para colunas ausentes

Sobrescreve toda a linha; colunas ausentes recebem null

Escolha um modo:

  • Use InsertOrIgnore quando a primeira gravação deve prevalecer e você não deseja sobrescrever dados existentes.

  • Use InsertOrUpdate se forem necessárias atualizações seletivas de colunas, preservando aquelas não especificadas.

  • Use InsertOrReplace para substituição completa da linha, definindo explicitamente as colunas ausentes como null.

Casos de uso

A instrução INSERT ON CONFLICT aplica-se à importação de dados por meio de instruções SQL.

Data Integration (DataWorks)

O Data Integration possui um recurso nativo de INSERT ON CONFLICT. Configure a Write Conflict Policy no Hologres Writer:

  • Ignore — equivalente a InsertOrIgnore

  • Replace — equivalente a InsertOrReplace

Essa política vale tanto para sincronização offline quanto em tempo real. Para atualizar dados durante a sincronização, a tabela do Hologres precisa ter uma chave primária.

Flink

A write conflict policy padrão para gravações via Flink é InsertOrIgnore. Esse comportamento exige uma chave primária na tabela de destino e mantém a primeira entrada ao encontrar duplicatas. Ao usar a sintaxe ctas, o padrão passa a ser InsertOrUpdate.

Sintaxe

INSERT INTO <table_name> [ AS <alias> ] [ ( <column_name> [, ...] ) ]
    { VALUES ( { <expression> } [, ...] ) [, ...] | <query> }
    [ ON CONFLICT [ conflict_target ] conflict_action ]

-- conflict_target
ON CONSTRAINT constraint_name

-- conflict_action
DO NOTHING
DO UPDATE SET { <column_name> = { <expression> } |
                ( <column_name> [, ...] ) = ( { <expression> } [, ...] ) |
              } [, ...]
          [ WHERE condition ]

Sobre excluded:

O termo excluded é um alias de tabela virtual que referencia a linha proposta para inserção — ou seja, a linha que originou o conflito. Não se trata de um alias para a tabela de origem. Use excluded.<column_name> para referenciar uma coluna específica da linha recebida ou ROW(excluded.*) para acessar todas as colunas na ordem definida no DDL.

Parâmetros:

Parâmetro

Descrição

table_name

Tabela de destino

alias

Nome alternativo para a tabela de destino

column_name

Coluna alvo na tabela de destino

DO NOTHING

InsertOrIgnore — ignora a inserção quando existe uma chave primária duplicada

DO UPDATE

InsertOrUpdate ou InsertOrReplace — atualiza a linha existente quando há uma chave primária duplicada

expression

Valor a ser gravado. Use excluded.<column_name> para referenciar o valor da linha recebida nessa coluna. Use ROW(excluded.*) para referenciar todas as colunas da linha recebida na ordem do DDL.

Funcionamento

A instrução INSERT ON CONFLICT segue o mesmo caminho interno de execução do UPDATE. O desempenho da atualização varia conforme o formato de armazenamento da tabela.

Tabelas orientadas a colunas

Tabelas sem chave primária oferecem o maior throughput de gravação. Para tabelas com chave primária:

InsertOrIgnore > InsertOrReplace >= InsertOrUpdate (full row) > InsertOrUpdate (partial column)

Tabelas orientadas a linhas

InsertOrReplace = InsertOrUpdate (full row) >= InsertOrUpdate (partial column) >= InsertOrIgnore

Para obter o melhor desempenho em tabelas orientadas a linhas, mantenha a ordem das colunas na cláusula DO UPDATE SET consistente com a cláusula INSERT e execute uma atualização de linha completa:

INSERT INTO test1 (a, b, c)
SELECT d, e, f FROM test2
ON CONFLICT (a)
DO UPDATE SET (a, b, c) = ROW(excluded.*);
Colunas com valores padrão não são atualizadas pelo DO UPDATE , o que reduz o desempenho. Para implementar InsertOrReplace via SQL, passe null explicitamente na lista VALUES . Ferramentas como Flink e Data Integration adicionam null automaticamente para colunas ausentes.

Limitações

  • A cláusula ON CONFLICT deve referenciar todas as colunas da chave primária.

  • Quando o Hologres High-QPS Engine (HQE) executa INSERT ON CONFLICT, a ordem das operações não é garantida — as semânticas de manter a primeira ou a última entrada não têm suporte. O comportamento adotado é manter qualquer uma. Para impor a retenção da última entrada em linhas duplicadas na origem, defina:

    set hg_experimental_affect_row_multiple_times_keep_last = on;
  • Os dados de origem não devem conter linhas duplicadas. A semântica do PostgreSQL exige que cada linha na lista VALUES ou na consulta de source seja única. Se a source contiver duplicatas (por exemplo, devido a uma subconsulta SELECT que retorna duas linhas com a mesma chave primária), a instrução falhará. Para evitar isso, remova as duplicatas na source antes da cláusula ON CONFLICT:

    -- Deduplicate the source with a subquery before upserting
    INSERT INTO target_table (a, b, c)
    SELECT a, MAX(b), MAX(c) FROM source_table GROUP BY a
    ON CONFLICT (a)
    DO UPDATE SET (a, b, c) = ROW(excluded.*);

Exemplos

Configurar a tabela de teste

BEGIN;
CREATE TABLE test1 (
    a int NOT NULL PRIMARY KEY,
    b int,
    c int
);
COMMIT;

INSERT INTO test1 VALUES (1, 2, 3);

Os exemplos abaixo são independentes entre si. Cada um parte do estado inicial apresentado acima.

InsertOrIgnore — ignorar em caso de conflito

Se existir uma chave primária duplicada, a linha recebida será descartada.

INSERT INTO test1 (a, b, c) VALUES (1, 1, 1)
ON CONFLICT (a)
DO NOTHING;

Resultado:

a | b | c
1 | 2 | 3    -- unchanged

InsertOrUpdate — atualizar colunas específicas

Somente as colunas listadas em SET são atualizadas. As colunas não listadas mantêm seus valores atuais.

-- Partial-column update: only b is updated, c keeps its value
INSERT INTO test1 (a, b, c) VALUES (1, 1, 1)
ON CONFLICT (a)
DO UPDATE SET b = excluded.b;

Resultado:

a | b | c
1 | 1 | 3    -- c unchanged

InsertOrUpdate — atualização de linha completa

Duas formas equivalentes:

-- Method 1: list all columns explicitly
INSERT INTO test1 (a, b, c) VALUES (1, 1, 1)
ON CONFLICT (a)
DO UPDATE SET b = excluded.b, c = excluded.c;

-- Method 2: use ROW(excluded.*) shorthand
INSERT INTO test1 (a, b, c) VALUES (1, 1, 1)
ON CONFLICT (a)
DO UPDATE SET (a, b, c) = ROW(excluded.*);

Resultado:

a | b | c
1 | 1 | 1

InsertOrReplace — sobrescrever com null para colunas ausentes

Para substituir uma linha inteira e preencher colunas ausentes com null, passe null explicitamente na lista VALUES.

INSERT INTO test1 (a, b, c) VALUES (1, 1, null)
ON CONFLICT (a)
DO UPDATE SET b = excluded.b, c = excluded.c;

Resultado:

a | b | c
1 | 1 | \N

Upsert a partir de outra tabela

-- Prepare source table
CREATE TABLE test2 (
    d int NOT NULL PRIMARY KEY,
    e int,
    f int
);
INSERT INTO test2 VALUES (1, 5, 6);

-- Replace rows in test1 with matching rows from test2
INSERT INTO test1 (a, b, c)
SELECT d, e, f FROM test2
ON CONFLICT (a)
DO UPDATE SET (a, b, c) = ROW(excluded.*);

Resultado:

a | b | c
1 | 5 | 6

Para remapear colunas (por exemplo, test2.e atualiza test1.c e test2.f atualiza test1.b):

INSERT INTO test1 (a, b, c)
SELECT d, e, f FROM test2
ON CONFLICT (a)
DO UPDATE SET (a, c, b) = ROW(excluded.*);

Resultado:

a | b | c
1 | 6 | 5

Solução de problemas

Erro: duplicate key value violates unique constraint / Update row with Key multiple times

Sintomas:

duplicate key value violates unique constraint
-- or --
Update row with Key (xxx)=(yyy) multiple times

Causa: Os dados de source contêm linhas duplicadas. O exemplo a seguir reproduz o erro:

-- This fails: the source contains (1, 2, 3) twice
INSERT INTO test1 VALUES (1, 2, 3), (1, 2, 3)
ON CONFLICT (a)
DO UPDATE SET (a, b, c) = ROW(excluded.*);
-- ERROR: internal error or constraint violation

Estado da tabela após o erro: test1 permanece inalterada.

Correção: Defina o sinalizador keep-last para resolver automaticamente as linhas duplicadas na source:

set hg_experimental_affect_row_multiple_times_keep_last = on;

Alternativamente, remova as duplicatas na source antes de executar o upsert (consulte Limitações).

Erro: chave duplicada causada por TTL expirado

Causa: Uma tabela de source tem um tempo de vida (TTL) configurado. Após a expiração do TTL, linhas obsoletas podem não ser limpas imediatamente, deixando chaves primárias duplicadas na source.

Correção: A partir do Hologres V1.3.23, use o comando a seguir para remover linhas com chave primária duplicada causadas por TTL expirado. A política padrão é keep-last. Este comando está disponível apenas no Hologres V1.3.23 e versões posteriores — se sua instância for de uma versão anterior, faça o upgrade primeiro.

Em princípio, chaves primárias não devem ser duplicadas. Este comando limpa apenas PKs duplicadas causadas por TTL expirado, e não duplicações gerais de PK.
call public.hg_remove_duplicated_pk('<schema>.<table_name>');

Exemplo:

BEGIN;
CREATE TABLE tbl_1 (a int NOT NULL PRIMARY KEY, b int, c int);
CREATE TABLE tbl_2 (d int NOT NULL PRIMARY KEY, e int, f int);
CALL set_table_property('tbl_2', 'time_to_live_in_seconds', '300');
COMMIT;

INSERT INTO tbl_1 VALUES (1, 1, 1), (2, 3, 4);
INSERT INTO tbl_2 VALUES (1, 5, 6);

-- After 300 seconds, insert a row with the same primary key into tbl_2.
INSERT INTO tbl_2 VALUES (1, 3, 6);

-- This fails: tbl_2 now has duplicate primary keys caused by TTL expiry.
INSERT INTO tbl_1 (a, b, c)
SELECT d, e, f FROM tbl_2
ON CONFLICT (a)
DO UPDATE SET (a, b, c) = ROW(excluded.*);
-- ERROR: internal error: Duplicate keys detected when building hash table.

-- Clean up duplicate primary keys in tbl_2, then retry.
call public.hg_remove_duplicated_pk('tbl_2');

-- The import now succeeds.
INSERT INTO tbl_1 (a, b, c)
SELECT d, e, f FROM tbl_2
ON CONFLICT (a)
DO UPDATE SET (a, b, c) = ROW(excluded.*);

Erro: falta de memória (OOM)

Sintoma:

Total memory used by all existing queries exceeded memory limitation

Causa: A instância não tem memória suficiente para uma tarefa de gravação de grande volume.

Correção:

Próximos passos