Tous les produits
Search
Centre de documentation

Hologres:INSERT ON CONFLICT (UPSERT)

Dernière mise à jour :Aug 11, 2026

INSERT ON CONFLICT offre une sémantique d'upsert atomique : insérez une ligne si aucun conflit n'existe, ou mettez à jour (ou ignorez) la ligne existante en cas de détection d'une clé primaire en double.

Utilisez cette instruction pour importer des données directement via SQL. Pour les écritures basées sur Data Integration et Flink, consultez la section Cas d'utilisation.

Concepts clés

L'instruction prend en charge trois modes de résolution des conflits :

Mode Clause SQL Comportement en cas de clé primaire en double
InsertOrIgnore DO NOTHING Ignore la ligne entrante ; la ligne existante reste inchangée
InsertOrUpdate DO UPDATE SET <columns> Met à jour uniquement les colonnes spécifiées ; les autres conservent leurs valeurs actuelles
InsertOrReplace DO UPDATE SET <all columns> avec null pour les colonnes manquantes Écrase la ligne entière ; les colonnes manquantes passent à null

Choix du mode :

  • Sélectionnez InsertOrIgnore lorsque la première écriture fait foi et que vous ne souhaitez jamais écraser les données existantes.

  • Sélectionnez InsertOrUpdate pour mettre à jour certaines colonnes tout en préservant les valeurs des colonnes non mentionnées.

  • Sélectionnez InsertOrReplace pour remplacer entièrement la ligne, en définissant explicitement les colonnes manquantes sur null.

Cas d'utilisation

INSERT ON CONFLICT s'applique lors de l'importation de données via des instructions SQL.

Data Integration (DataWorks)

Data Integration intègre nativement la fonctionnalité INSERT ON CONFLICT . Configurez la stratégie Write Conflict Policy dans le connecteur Hologres Writer :

  • Ignore — équivalent à InsertOrIgnore

  • Replace — équivalent à InsertOrReplace

Cette stratégie s'applique à la synchronisation hors ligne et en temps réel. La table Hologres doit disposer d'une clé primaire pour permettre la mise à jour des données lors de la synchronisation.

Flink

La stratégie write conflict policy par défaut pour les écritures Flink est InsertOrIgnore . Elle nécessite une clé primaire sur la table de destination (sink) et conserve la première entrée en cas de doublon. Avec la syntaxe ctas , la valeur par défaut devient InsertOrUpdate .

Syntaxe

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 ]

À propos de excluded :

excluded est un alias de table virtuelle qui référence la ligne proposée à l'insertion, c'est-à-dire celle ayant déclenché le conflit. Ce n'est pas un alias de la table source. Utilisez excluded.<column_name> pour référencer une colonne spécifique de la ligne entrante, ou ROW(excluded.*) pour référencer toutes les colonnes dans l'ordre DDL.

Paramètres :

Paramètre Description
table_name Table de destination
alias Nom alternatif pour la table de destination
column_name Colonne cible dans la table de destination
DO NOTHING InsertOrIgnore : ignore l'insertion en cas de clé primaire en double
DO UPDATE InsertOrUpdate ou InsertOrReplace : met à jour la ligne existante en cas de clé primaire en double
expression Valeur à écrire. Utilisez excluded.<column_name> pour référencer la valeur de la colonne correspondante dans la ligne entrante. Utilisez ROW(excluded.*) pour référencer toutes les colonnes de la ligne entrante dans l'ordre DDL.

Fonctionnement

INSERT ON CONFLICT suit le même chemin interne que UPDATE. Les performances de mise à jour dépendent du format de stockage de la table.

Tables orientées colonne

Les tables sans clé primaire offrent le débit d'écriture le plus élevé. Pour les tables avec une clé primaire :

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

Tables orientées ligne

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

Pour optimiser les performances sur les tables orientées ligne, assurez-vous que l'ordre des colonnes dans la clause DO UPDATE SET correspond à celui de la clause INSERT et effectuez une mise à jour complète de la ligne :

INSERT INTO test1 (a, b, c)
SELECT d, e, f FROM test2
ON CONFLICT (a)
DO UPDATE SET (a, b, c) = ROW(excluded.*);
Les colonnes dotées de valeurs par défaut ne sont pas mises à jour par DO UPDATE , ce qui réduit les performances. Pour implémenter InsertOrReplace via SQL, transmettez null explicitement dans la liste VALUES . Des outils comme Flink et Data Integration ajoutent null automatiquement pour les colonnes manquantes.

Limitations

  • La clause ON CONFLICT doit référencer toutes les colonnes de la clé primaire.

  • Lorsque le moteur High-QPS Engine (HQE) de Hologres exécute INSERT ON CONFLICT , l'ordre des opérations n'est pas garanti : les sémantiques « conserver le premier » et « conserver le dernier » ne sont pas prises en charge. Le comportement est « conserver n'importe lequel ». Pour forcer la conservation de la dernière ligne en cas de doublons dans la source, définissez :

    set hg_experimental_affect_row_multiple_times_keep_last = on;
  • Les données source ne doivent pas contenir de lignes en double. La sémantique PostgreSQL exige l'unicité de chaque ligne dans la liste VALUES ou la requête source. Si la source contient des doublons (par exemple, une sous-requête SELECT renvoyant deux lignes avec la même clé primaire), l'instruction échoue. Pour éviter cela, dédupliquez la source avant la clause 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.*);

Exemples

Configuration de la table de test

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

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

Les exemples ci-dessous sont indépendants. Chacun part de l'état initial décrit ci-dessus.

InsertOrIgnore — ignorer en cas de conflit

En cas de clé primaire en double, la ligne entrante est ignorée.

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

Résultat :

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

InsertOrUpdate — mise à jour de colonnes spécifiques

Seules les colonnes listées dans SET sont mises à jour. Les autres conservent leurs valeurs actuelles.

-- 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;

Résultat :

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

InsertOrUpdate — mise à jour de ligne complète

Deux formes équivalentes :

-- 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.*);

Résultat :

a | b | c
1 | 1 | 1

InsertOrReplace — écrasement avec null pour les colonnes manquantes

Pour remplacer une ligne entière et remplir les colonnes manquantes avec null , transmettez null explicitement dans la liste VALUES .

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

Résultat :

a | b | c
1 | 1 | \N

Upsert depuis une autre table

-- 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.*);

Résultat :

a | b | c
1 | 5 | 6

Pour remapper les colonnes (par exemple, test2.e met à jour test1.c, et test2.f met à jour 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.*);

Résultat :

a | b | c
1 | 6 | 5

Dépannage

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

Symptômes :

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

Cause : Les données source contiennent des lignes en double. L'exemple suivant reproduit l'erreur :

-- 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

État de la table après l'erreur : test1 reste inchangée.

Solution : Définissez l'indicateur keep-last pour résoudre automatiquement les lignes source en double :

set hg_experimental_affect_row_multiple_times_keep_last = on;

Vous pouvez également dédupliquer la source avant l'upsert (voir la section Limitations).

Erreur : clé en double causée par l'expiration du TTL

Cause : Une table source est configurée avec une durée de vie (TTL). Après l'expiration du TTL, les lignes obsolètes peuvent ne pas être nettoyées immédiatement, ce qui laisse des clés primaires en double dans la source.

Solution : À partir de Hologres V1.3.23, utilisez la commande suivante pour supprimer les lignes de clé primaire en double causées par l'expiration du TTL. La stratégie par défaut est « conserver le dernier ». Cette commande est disponible uniquement dans Hologres V1.3.23 et versions ultérieures ; si votre instance utilise une version antérieure, effectuez d'abord la mise à niveau.

En principe, les clés primaires ne doivent pas être dupliquées. Cette commande nettoie uniquement les clés primaires en double causées par l'expiration du TTL, et non la duplication générale des clés primaires.
call public.hg_remove_duplicated_pk('<schema>.<table_name>');

Exemple :

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.*);

Erreur : out-of-memory (OOM)

Symptôme :

Total memory used by all existing queries exceeded memory limitation

Cause : L'instance ne dispose pas de suffisamment de mémoire pour une tâche d'écriture volumineuse.

Solution :

Étapes suivantes

  • UPDATE — comprendre le mécanisme de mise à jour sous-jacent

  • Aperçu de Serverless Computing — décharger les tâches d'écriture volumineuses vers des ressources serverless (Hologres V2.1.17+)

  • Hologres Writer — configurer la stratégie de conflit d'écriture dans Data Integration