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 parDO UPDATE, ce qui réduit les performances. Pour implémenter InsertOrReplace via SQL, transmetteznullexplicitement dans la listeVALUES. Des outils comme Flink et Data Integration ajoutentnullautomatiquement pour les colonnes manquantes.
Limitations
La clause
ON CONFLICTdoit 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
VALUESou la requête source. Si la source contient des doublons (par exemple, une sous-requêteSELECTrenvoyant deux lignes avec la même clé primaire), l'instruction échoue. Pour éviter cela, dédupliquez la source avant la clauseON 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 :
Utilisez Serverless Computing (Hologres V2.1.17+) pour décharger la tâche vers des ressources serverless, réduisant ainsi le risque d'OOM sans réserver de capacité supplémentaire sur l'instance.
Suivez les étapes décrites dans Dépannage des erreurs OOM courantes.
É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