web-dev-qa-db-fra.com

Renvoyer l'identifiant après insertion ou sélectionnez

Je veux construire une fonction qui insérera un courrier électronique si la valeur de l'e-mail n'existe pas dans la table et renvoie le email_id de la rangée. Comment puis-je faire ceci?
[.____] aussi Comment puis-je renvoyer l'identifiant si le courrier électronique n'a pas été inséré et qu'il existe déjà dans la DB? Dois-je effectuer un autre SELECT?

BEGIN;
  LOCK TABLE mailing_list IN SHARE ROW EXCLUSIVE MODE;
  INSERT INTO mailing_list (email)
  SELECT 'email'
   WHERE NOT EXISTS (
     SELECT * FROM mailing_list WHERE email='email'
   );
COMMIT;

J'ai essayé d'ajouter le returning id mais ça ne marche pas. J'ai eu:

la requête n'a pas de destination pour les données de résultat

sqlfiddle

2
RockNinja

@ a_horse déjà expliqué Comment éviter le message d'erreur que vous avez vu.

Voici une variante simple de la version associée que nous faisons référence à:

CREATE OR REPLACE FUNCTION f_email_insel(_email text, OUT email_id int) AS
$func$
BEGIN

LOOP
   BEGIN  -- start inner block inside loop to handle possible exception

   SELECT INTO email_id  m.email_id FROM mailing_list m WHERE m.email = _email;

   IF NOT FOUND THEN
      INSERT INTO mailing_list (email) VALUES (_email)
      RETURNING mailing_list.email_id INTO email_id;
   END IF;

   EXCEPTION WHEN UNIQUE_VIOLATION THEN     -- inserted in concurrent session.
      RAISE NOTICE 'It actually happened!'; -- hardly ever happens
   END;

   EXIT WHEN email_id IS NOT NULL;          -- else keep looping
END LOOP;

END
$func$ LANGUAGE plpgsql;

SQL FIDDLE.

Vous n'avez besoin que de la boucle pour faire face à une condition de race possible: si une transaction simultanée écrit la même valeur email la valeur comprise entre SELECT et INSERT, vous auriez une violation unique - qui est traité correctement ici. Cela suppose une contrainte UNIQUE (ou un indice UNIQUE) sur email, évidemment.

Le alternative avec CTES fonctionne comme une Déclaration SQL. Voici donc que les frais généraux sont légèrement plus petits (des requêtes plus simples), mais le délai d'une condition de course est légèrement plus gros. Surtout si la ligne existe souvent déjà, ceci est un peu plus rapide.
[.____] Lire l'explication détaillée là-bas et choisir l'approche qui convient le mieux à votre cas d'utilisation.

3
Erwin Brandstetter