Gibt es eine einfache Möglichkeit in PL / pgSQL zu überprüfen, ob eine Abfrage kein Ergebnis liefert?

15

Ich experimentiere gerade ein bisschen mit PL / pgSQL und möchte wissen, ob es eine elegantere Möglichkeit gibt, so etwas zu tun:

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
quelle

Antworten:

20

Ausnahmeblöcke dienen zum Abfangen von Fehlern und nicht zum Überprüfen von Bedingungen. Mit anderen Worten, wenn eine Bedingung zur Kompilierzeit behandelt werden kann, sollte sie nicht als Fehler abgefangen, sondern von einer normalen Programmlogik gelöst werden.

Im Abschnitt "Überfüllungsfehler" der PL / PgSQL-Dokumentation finden Sie einen solchen Tipp:

Tipp: Die Eingabe und das Verlassen eines Blocks mit einer EXCEPTION-Klausel sind erheblich teurer als bei einem Block ohne eine EXCEPTION-Klausel. Verwenden Sie EXCEPTION daher nicht ohne Notwendigkeit.

Anstatt Ausnahmen (schlecht) oder IF / THEN / ELSIF (besser) zu verwenden, können Sie dies in eine Abfrage umschreiben:

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;

Wenn Sie wirklich zwei Abfragen möchten, können Sie mit der speziellen FOUND-Variablen testen, ob die vorherige Abfrage ein Ergebnis liefert:

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;

Obligatorische RTFM-Links folgen :-)

Siehe hierzu die Beschreibung der FOUNDVariablen und dies für IF/ THENblocks.

filiprem
quelle
13

Sie können eine spezielle Variable FOUND vom Typ Boolean untersuchen. Aus der Dokumentation:

FOUND fängt bei jedem PL / pgSQL-Funktionsaufruf mit false an. Es wird von jedem der folgenden Anweisungstypen festgelegt:

Eine SELECT INTO-Anweisung setzt FOUND auf true, wenn eine Zeile zugewiesen ist, und auf false, wenn keine Zeile zurückgegeben wird.

Eine PERFORM-Anweisung setzt FOUND auf true, wenn sie eine oder mehrere Zeilen erzeugt (und verwirft), false, wenn keine Zeile erzeugt wird.

UPDATE-, INSERT- und DELETE-Anweisungen setzen FOUND auf true, wenn mindestens eine Zeile betroffen ist, false, wenn keine Zeile betroffen ist.

Eine FETCH-Anweisung setzt FOUND auf true, wenn eine Zeile zurückgegeben wird, und auf false, wenn keine Zeile zurückgegeben wird.

Eine MOVE-Anweisung setzt FOUND auf true, wenn der Cursor erfolgreich neu positioniert wurde, andernfalls auf false.

Eine FOR- oder FOREACH-Anweisung setzt FOUND auf true, wenn sie einmal oder mehrmals wiederholt wird, andernfalls auf false. FOUND wird beim Verlassen der Schleife auf diese Weise eingestellt. Während der Ausführung der Schleife wird FOUND von der Anweisung loop nicht geändert, obwohl dies möglicherweise durch die Ausführung anderer Anweisungen innerhalb des Schleifenkörpers geändert wird.

Die Anweisungen RETURN QUERY und RETURN QUERY EXECUTE setzen FOUND auf true, wenn die Abfrage mindestens eine Zeile zurückgibt, und auf false, wenn keine Zeile zurückgegeben wird.

Andere PL / pgSQL-Anweisungen ändern den Status von FOUND nicht. Beachten Sie insbesondere, dass EXECUTE die Ausgabe von GET DIAGNOSTICS ändert, jedoch nicht FOUND.

FOUND ist eine lokale Variable in jeder PL / pgSQL-Funktion. Änderungen daran wirken sich nur auf die aktuelle Funktion aus.

alexk
quelle
Aber select intowenn keine Daten zurückgegeben werden, wird trotzdem eine Ausnahme ausgelöst, oder?
Jack Douglas
3
Im Allgemeinen nein, es werden nur Ausnahmen ausgelöst, wenn die STRICT-Klausel angegeben ist, wie SELECT * INTO STRICT my record ...
alexk
ach ja, meine schlechte - aber bedeutet das nicht, dass der Exception-Handler im OP-Beispiel niemals feuern wird? :-)
Jack Douglas
1
@JackDouglas: Keine Daten sind im Allgemeinen kein Grund für eine Ausnahme (mit Ausnahme von Sonderfällen wie dem obigen STRICT-Modifikator). Die OP hatte dort ein Missverständnis.
Erwin Brandstetter