INSERT ON CONFLICT offre une sémantique d'upsert atomique : insérez une ligne en l'absence de conflit, ou mettez à jour (ou ignorez) la ligne existante si une clé primaire en double est détectée.
Utilisez cette instruction pour importer des données directement via SQL. Pour les écritures basées sur Data Integration et Flink, consultez 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 deviennent null |
Choisissez un mode :
Utilisez InsertOrIgnore lorsque la première écriture prime et que vous ne souhaitez jamais écraser les données existantes.
Utilisez InsertOrUpdate pour mettre à jour sélectivement certaines colonnes tout en préservant les autres.
Utilisez 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 Write Conflict Policy dans le Hologres Writer :
Ignore — équivalent à InsertOrIgnore
Replace — équivalent à InsertOrReplace
Cette politique 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 pendant la synchronisation.
Flink
La write conflict policy par défaut pour les écritures Flink est InsertOrIgnore. Cela nécessite une clé primaire sur la table de destination et conserve la première entrée en cas de doublon. Lorsque vous utilisez 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 fait référence à la ligne proposée pour l'insertion, c'est-à-dire celle qui a 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, alignez l'ordre des colonnes dans la clause DO UPDATE SET sur 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, passeznullexplicitement 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 que chaque ligne de la liste
VALUESou de la requête source soit unique. 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
Configurer 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
Si une clé primaire en double existe, 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 — mettre à jour des 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 — écraser avec null pour les colonnes manquantes
Pour remplacer une ligne entière et remplir les colonnes manquantes avec null, passez 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 Limitations).
Erreur : clé en double causée par un TTL expiré
Cause : Une table source est configurée avec une durée de vie (TTL). Après l'expiration du TTL, les anciennes lignes peuvent ne pas être nettoyées immédiatement, laissant des clés primaires en double dans la source.
Solution : À partir de Hologres V1.3.23, vous pouvez utiliser la commande suivante pour supprimer les lignes de clé primaire en double causées par un TTL expiré. La politique par défaut est keep-last. 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 un TTL expiré, 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é d'instance supplémentaire.
Suivez les étapes décrites dans Dépanner les erreurs OOM courantes.
Étapes suivantes
UPDATE — comprendre le mécanisme de mise à jour sous-jacent
Aperçu de Serverless Computing — décharger les grandes tâches d'écriture vers des ressources serverless (Hologres V2.1.17+)
Hologres Writer — configurer la politique de conflit d'écriture dans Data Integration