J'ai une table tag
avec 2 colonnes: id
(uuid) et name
(texte). Je veux maintenant insérer une nouvelle balise dans la table, mais si la balise existe déjà, je veux simplement obtenir le id
de l'enregistrement existant.
J'ai supposé que je pouvais simplement utiliser ON CONFLICT DO NOTHING
en combinaison avec RETURNING "id"
:
INSERT INTO
"tag" ("name")
VALUES( 'foo' )
ON CONFLICT DO NOTHING
RETURNING "id";
Mais cela renvoie un jeu de résultats vide, si la balise portant le nom "foo" existe déjà.
J'ai ensuite modifié la requête pour utiliser un noop DO UPDATE
clause:
INSERT INTO
"tag" ("name")
VALUES( 'foo' )
ON CONFLICT ("name") DO UPDATE SET "name" = 'foo'
RETURNING "id";
Cela fonctionne comme prévu, mais c'est un peu déroutant, car je ne fais que définir le nom sur la valeur déjà existante.
Est-ce la façon de régler ce problème ou y a-t-il une approche plus simple qui me manque?
Cela fonctionnera (pour autant que j'ai testé) dans les 3 cas, si les valeurs à insérer sont toutes nouvelles ou toutes déjà dans le tableau ou un mélange:
WITH
val (name) AS
( VALUES -- rows to be inserted
('foo'),
('bar'),
('zzz')
),
ins AS
( INSERT INTO
tag (name)
SELECT name FROM val
ON CONFLICT (name) DO NOTHING
RETURNING id, name -- only the inserted ones
)
SELECT COALESCE(ins.id, tag.id) AS id,
val.name
FROM val
LEFT JOIN ins ON ins.name = val.name
LEFT JOIN tag ON tag.name = val.name ;
Il existe probablement d'autres façons de procéder, peut-être sans utiliser le nouveau ON CONFLICT
syntaxe.
Aucune idée de la façon dont cela fonctionnera, mais juste comme une autre option à essayer, voici la même chose à l'ancienne (sans ON CONFLICT
):
WITH items (name) AS (VALUES ('foo'), ('bar'), ('zzz')),
added AS
(
INSERT INTO tag (name)
SELECT name FROM items
EXCEPT
SELECT name FROM tag
RETURNING id
)
SELECT id FROM added
UNION ALL
SELECT id FROM tag
WHERE name IN (SELECT name FROM items)
;
Autrement dit, insérez uniquement les noms [uniques] introuvables dans la table tag
et renvoyez les ID; combinez cela avec les ID des noms qui existent dans tag
, pour la sortie finale. Vous pouvez également lancer name
dans la sortie, comme suggéré par ypercubeᵀᴹ , afin de savoir quel ID correspond à quel nom.