Existe-t-il un moyen simple en PL / pgSQL de vérifier si une requête n'a retourné aucun résultat?

16

J'expérimente actuellement un peu avec PL / pgSQL et je veux savoir s'il existe une façon plus élégante de faire quelque chose comme ça:

select c.data into data from doc c where c.doc_id = id and c.group_cur > group_cur order by c.id desc limit 1;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        select c.data into data from doc c where c.doc_id = id and c.global_cur > global_cur order by c.id desc limit 1;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RETURN NULL;
icefex
la source

Réponses:

21

Les blocs d'exception sont destinés à intercepter les erreurs, pas à vérifier les conditions. En d'autres termes, si une condition peut être gérée au moment de la compilation, elle ne doit pas être piégée en tant qu'erreur mais résolue par une logique de programme ordinaire.

Dans la section Trapping Errors de la documentation PL / PgSQL, vous pouvez trouver une telle astuce:

Conseil: Un bloc contenant une clause EXCEPTION est beaucoup plus cher à entrer et à quitter qu'un bloc sans un. Par conséquent, n'utilisez pas EXCEPTION sans besoin.

Au lieu d'utiliser des exceptions (mauvaises) ou IF / THEN / ELSIF (mieux), vous pouvez réécrire ceci dans une requête:

SELECT c.data into data
FROM  doc c
WHERE c.doc_id = id
  and (
    c.group_cur > group_cur
    or
    c.global_cur > global_cur
  )
ORDER BY
  -- this will make group always preferred over global
  case when c.group_cur > group_cur then 1 else 2 end ASC,
  -- and this is your normal ordering
  c.id DESC
limit 1;

Si vous voulez vraiment deux requêtes, vous pouvez utiliser une variable FOUND spéciale pour tester si la requête précédente a donné un résultat:

select c.data into data
from doc c
where c.doc_id = id and c.group_cur > group_cur
order by c.id desc limit 1;
if not found then
    select c.data into data
    from doc c
    where c.doc_id = id and c.global_cur > global_cur
    order by c.id desc limit 1;
    if not found then return null; end if;
end if;

Liens RTFM obligatoires suivis :-)

Voir ceci pour la description de la FOUNDvariable, et ceci pour IF/ THENblocks.

filiprem
la source
13

Vous pouvez examiner une variable spéciale FOUND d'un type booléen. De la documentation:

FOUND commence faux dans chaque appel de fonction PL / pgSQL. Il est défini par chacun des types d'instructions suivants:

Une instruction SELECT INTO définit FOUND true si une ligne est affectée, false si aucune ligne n'est renvoyée.

Une instruction PERFORM définit FOUND true si elle produit (et supprime) une ou plusieurs lignes, false si aucune ligne n'est produite.

Les instructions UPDATE, INSERT et DELETE définissent FOUND true si au moins une ligne est affectée, false si aucune ligne n'est affectée.

Une instruction FETCH définit FOUND true si elle renvoie une ligne, false si aucune ligne n'est renvoyée.

Une instruction MOVE définit FOUND true si elle repositionne correctement le curseur, false dans le cas contraire.

Une instruction FOR ou FOREACH définit FOUND true si elle itère une ou plusieurs fois, sinon false. FOUND est défini de cette façon lorsque la boucle se termine; à l'intérieur de l'exécution de la boucle, FOUND n'est pas modifié par l'instruction de boucle, bien qu'il puisse être modifié par l'exécution d'autres instructions dans le corps de la boucle.

Les instructions RETURN QUERY et RETURN QUERY EXECUTE définissent FOUND true si la requête renvoie au moins une ligne, false si aucune ligne n'est renvoyée.

Les autres instructions PL / pgSQL ne modifient pas l'état de FOUND. Notez en particulier qu'EXECUTE modifie la sortie de GET DIAGNOSTICS, mais ne change pas FOUND.

FOUND est une variable locale dans chaque fonction PL / pgSQL; ses modifications n'affectent que la fonction actuelle.

alexk
la source
mais un select intoqui ne renvoie aucune donnée lèvera toujours une exception, non?
Jack dit d'essayer topanswers.xyz le
3
généralement non, il ne déclenche des exceptions que si la clause STRICT est spécifiée, comme SELECT * INTO STRICT mon enregistrement ...
alexk
ah oui, ma mauvaise - mais cela ne signifie-t-il pas que le gestionnaire d'exceptions dans l'exemple OP ne se déclenchera jamais? :-)
Jack dit d'essayer topanswers.xyz le
1
@JackDouglas: Aucune donnée n'est généralement pas une cause d'exception (sauf pour des cas spéciaux comme le modificateur STRICT ci-dessus). Le PO a eu une idée fausse là-bas.
Erwin Brandstetter