FirebirdSQL logo

Die RETURNING-Klausel

Eine MERGE-Anweisung, die höchstens eine Zeile betrifft, kann eine RETURNING-Klausel enthalten, um hinzugefügte, geänderte oder entfernte Werte zurückzugeben.Wenn eine RETURNING-Klausel vorhanden ist und mehr als ein übereinstimmender Datensatz gefunden wird, wird ein Fehler “multiple rows in singleton select” ausgegeben.Die RETURNING-Klausel kann beliebige Spalten aus der Zieltabelle (oder aktualisierbaren View) sowie andere Spalten (zB aus der Quelle) und Ausdrücke enthalten.

Der Benutzer, der die Anweisung ausführt, benötigt SELECT-Berechtigungen für die in der RETURNING-Klausel angegebenen Spalten.

Die optionale Unterklausel INTO ist nur in PSQL gültig.

Note

Die Einschränkung, dass RETURNING nur mit einer Anweisung verwendet werden kann, die höchstens eine Zeile betrifft, könnte in einer zukünftigen Version entfernt werden.

Spaltennamen können durch das Präfix "OLD" oder "NEW" qualifiziert werden, um genau zu definieren, welcher Wert zurückgegeben werden soll: vor oder nach der Änderung. Die zurückgegebenen Werte enthalten die Änderungen, die von BEFORE-Triggern vorgenommen wurden.

Die Syntax der returning_list ähnelt der Spaltenliste einer SELECT-Klausel.Es ist möglich, alle Spalten mit * oder table_name.*, NEW.* und/oder OLD.* zu referenzieren.

Für die Aktion UPDATE oder INSERT verhalten sich unqualifizierte Spaltennamen oder solche, die durch den Zieltabellennamen oder Alias qualifiziert sind, als ob sie durch NEW qualifiziert wären, während sie für die DELETE Aktion wie durch OLD qualifiziert wären.

Das folgende Beispiel modifiziert das vorherige Beispiel, um eine Zeile zu betreffen, und fügt eine RETURNING-Klausel hinzu, um die alte und neue Warenmenge sowie die Differenz zwischen diesen Werten zurückzugeben.

Verwendung von MERGE mit einer RETURNING-Klausel
MERGE INTO PRODUCT_INVENTORY AS TARGET
USING (
  SELECT
    SL.ID_PRODUCT,
    SUM(SL.QUANTITY)
  FROM SALES_ORDER_LINE SL
  JOIN SALES_ORDER S ON S.ID = SL.ID_SALES_ORDER
  WHERE S.BYDATE = CURRENT_DATE
  AND SL.ID_PRODUCT =: ID_PRODUCT
  GROUP BY 1
) AS SRC (ID_PRODUCT, QUANTITY)
ON TARGET.ID_PRODUCT = SRC.ID_PRODUCT
WHEN MATCHED AND TARGET.QUANTITY - SRC.QUANTITY <= 0 THEN
  DELETE
WHEN MATCHED THEN
  UPDATE SET
    TARGET.QUANTITY = TARGET.QUANTITY - SRC.QUANTITY,
    TARGET.BYDATE = CURRENT_DATE
RETURNING OLD.QUANTITY, NEW.QUANTITY, SRC.QUANTITY
INTO : OLD_QUANTITY, :NEW_QUANTITY, :DIFF_QUANTITY

Beispiele für MERGE

  1. Aktualisieren Sie Bücher, wenn vorhanden, oder fügen Sie einen neuen Datensatz hinzu, wenn Sie abwesend sind

    MERGE INTO books b
      USING purchases p
      ON p.title = b.title and p.type = 'bk'
      WHEN MATCHED THEN
        UPDATE SET b.desc = b.desc || '; ' || p.desc
      WHEN NOT MATCHED THEN
        INSERT (title, desc, bought) values (p.title, p.desc, p.bought);
  2. Verwenden einer abgeleiteten Tabelle

    MERGE INTO customers c
      USING (SELECT * from customers_delta WHERE id > 10) cd
      ON (c.id = cd.id)
      WHEN MATCHED THEN
        UPDATE SET name = cd.name
      WHEN NOT MATCHED THEN
        INSERT (id, name) values (cd.id, cd.name);
  3. Zusammen mit einem rekursiven CTE

    MERGE INTO numbers
      USING (
        WITH RECURSIVE r(n) AS (
          SELECT 1 FROM rdb$database
          UNION ALL
          SELECT n+1 FROM r WHERE n < 200
        )
        SELECT n FROM r
      ) t
      ON numbers.num = t.n
      WHEN NOT MATCHED THEN
        INSERT(num) VALUES(t.n);
  4. Verwenden der DELETE-Klausel

    MERGE INTO SALARY_HISTORY
    USING (
      SELECT EMP_NO
      FROM EMPLOYEE
      WHERE DEPT_NO = 120) EMP
    ON SALARY_HISTORY.EMP_NO = EMP.EMP_NO
    WHEN MATCHED THEN DELETE
  5. Im folgenden Beispiel wird die Tabelle "PRODUCT_INVENTORY" täglich basierend auf den in der Tabelle "SALES_ORDER_LINE" verarbeiteten Bestellungen aktualisiert.Wenn der Lagerbestand des Produkts auf null oder darunter sinken würde, wird die Zeile für dieses Produkt aus der Tabelle PRODUCT_INVENTORY entfernt.

    MERGE INTO PRODUCT_INVENTORY AS TARGET
    USING (
      SELECT
        SL.ID_PRODUCT,
        SUM (SL.QUANTITY)
      FROM SALES_ORDER_LINE SL
      JOIN SALES_ORDER S ON S.ID = SL.ID_SALES_ORDER
      WHERE S.BYDATE = CURRENT_DATE
      GROUP BY 1
    ) AS SRC (ID_PRODUCT, QUANTITY)
    ON TARGET.ID_PRODUCT = SRC.ID_PRODUCT
    WHEN MATCHED AND TARGET.QUANTITY - SRC.QUANTITY <= 0 THEN
      DELETE
    WHEN MATCHED THEN
      UPDATE SET
        TARGET.QUANTITY = TARGET.QUANTITY - SRC.QUANTITY,
        TARGET.BYDATE = CURRENT_DATE

docnext count = 6

EXECUTE PROCEDURE

Verwendet für

Ausführen einer gespeicherten Prozedur

Verfügbar in

DSQL, ESQL, PSQL

Syntax
EXECUTE PROCEDURE procname
   [{ <inparam-list | ( <inparam-list> ) }]
   [RETURNING_VALUES { <outvar-list> | ( <outvar-list ) }]

<inparam-list> ::=
  <inparam> [, <inparam> ...]

<outvar-list> ::=
  <outvar> [, <outvar> ...]

<outvar> ::= [:]varname
Table 1. Arguments for the EXECUTE PROCEDURE-Anweisungsparameter
Argument Beschreibung

procname

Name der gespeicherten Prozedur

inparam

Ein Ausdruck, der den deklarierten Datentyp eines Eingabeparameters auswertet

varname

Eine PSQL-Variable, um den Rückgabewert zu erhalten

Führt eine ausführbare gespeicherte Prozedur aus, nimmt eine Liste mit einem oder mehreren Eingabeparametern, falls diese für die Prozedur definiert sind, und gibt einen einzeiligen Satz von Ausgabewerten zurück, wenn sie für die Prozedur definiert sind.

“Executable” Stored Procedure

Die EXECUTE PROCEDURE-Anweisung wird am häufigsten verwendet, um den Stil gespeicherter Prozeduren aufzurufen, die geschrieben werden, um auf der Serverseite eine Aufgabe zur Datenänderung auszuführen – solche, die keine SUSPEND-Anweisungen in ihrem Code enthalten.Sie können so konzipiert sein, dass sie eine Ergebnismenge, die nur aus einer Zeile besteht, die normalerweise über einen Satz von RETURNING_VALUES()-Variablen an eine andere gespeicherte Prozedur übergeben wird, die sie aufruft, zurückgeben.Clientschnittstellen verfügen normalerweise über einen API-Wrapper, der die Ausgabewerte in einen Einzelzeilenpuffer abrufen kann, wenn EXECUTE PROCEDURE in DSQL aufgerufen wird.

Das Aufrufen des anderen Stils von Stored Procedures - einer “selectable” - ist mit EXECUTE PROCEDURE möglich, aber es gibt nur die erste Zeile eines Ausgabesatzes zurück, der mit ziemlicher Sicherheit mehrzeilig ist.Auswählbare gespeicherte Prozeduren sind so konzipiert, dass sie durch eine SELECT-Anweisung aufgerufen werden und eine Ausgabe erzeugen, die sich wie eine virtuelle Tabelle verhält.

Note
  • In PSQL und DSQL können Eingabeparameter jeder Ausdruck sein, der in den erwarteten Typ aufgelöst wird.

  • Obwohl nach dem Namen der gespeicherten Prozedur keine Klammern erforderlich sind, um die Eingabeparameter einzuschließen, wird ihre Verwendung aus Gründen der guten Verwaltung empfohlen.

  • Wenn in einer Prozedur Ausgabeparameter definiert wurden, kann die `RETURNING_VALUES'-Klausel in PSQL verwendet werden, um sie in eine Liste zuvor deklarierter Variablen abzurufen, die in Reihenfolge, Datentyp und Anzahl mit den definierten Ausgabeparametern übereinstimmt.

  • Die Liste der RETURNING_VALUES kann optional in Klammern eingeschlossen werden und ihre Verwendung wird empfohlen.

  • Wenn DSQL-Anwendungen EXECUTE PROCEDURE unter Verwendung der Firebird-API oder einer Form von Wrapper dafür aufrufen, wird ein Puffer zum Empfangen der Ausgabezeile vorbereitet und die RETURNING_VALUES-Klausel wird nicht verwendet.

Beispiele für EXECUTE PROCEDURE

  1. In PSQL mit optionalen Doppelpunkten und ohne optionale Klammern:

    EXECUTE PROCEDURE MakeFullName
      :FirstName, :MiddleName, :LastName
      RETURNING_VALUES :FullName;
  2. In Firebirds Befehlszeilen-Dienstprogramm isql, mit Literalparametern und optionalen Klammern:

    EXECUTE PROCEDURE MakeFullName ('J', 'Edgar', 'Hoover');
    Note

    In DSQL (zB in isql) wird RETURNING_VALUES nicht verwendet.Eventuelle Ausgabewerte werden von der Anwendung erfasst und automatisch angezeigt.

  3. Ein PSQL-Beispiel mit Ausdrucksparametern und optionalen Klammern:

    EXECUTE PROCEDURE MakeFullName
      ('Mr./Mrs. ' || FirstName, MiddleName, upper(LastName))
      RETURNING_VALUES (FullName);

EXECUTE BLOCK

Verwendet für

Erstellen eines "anonymen" Blocks von PSQL-Code in DSQL zur sofortigen Ausführung

Verfügbar in

DSQL

Syntax
EXECUTE BLOCK [(<inparams>)]
  [RETURNS (<outparams>)]
  <psql-module-body>

<inparams> ::=  <param_decl> = ? [, <inparams> ]

<outparams> ::=  <param_decl> [, <outparams>]

<param_decl> ::=
  paramname <domain_or_non_array_type> [NOT NULL] [COLLATE collation]

<domain_or_non_array_type> ::=
  !! Siehe auch Skalardatentypen-Syntax !!

<psql-module-body> ::=
  !! Siehe auch Syntax für Modul-Bodys !!
Table 1. Argumente für die EXECUTE BLOCK-Anweisungsparameter
Argument Beschreibung

param_decl

Name und Beschreibung eines Eingabe- oder Ausgabeparameters

paramname

Der Name eines Eingangs- oder Ausgangsparameters des Verfahrensblocks, bis zu 63 Zeichen lang.Der Name muss unter Ein- und Ausgabeparametern und lokalen Variablen im Block eindeutig sein

collation

Sortierreihenfolge

Führt einen Block von PSQL-Code wie eine gespeicherte Prozedur aus, optional mit Eingabe- und Ausgabeparametern und Variablendeklarationen.Dies ermöglicht dem Benutzer, PSQL "on-the-fly" in einem DSQL-Kontext auszuführen.

Beispiele

  1. In diesem Beispiel werden die Zahlen 0 bis 127 und die entsprechenden ASCII-Zeichen in die Tabelle ASCIITABLE eingefügt:

    EXECUTE BLOCK
    AS
    declare i INT = 0;
    BEGIN
      WHILE (i < 128) DO
      BEGIN
        INSERT INTO AsciiTable VALUES (:i, ascii_char(:i));
        i = i + 1;
      END
    END
  2. Das nächste Beispiel berechnet das geometrische Mittel zweier Zahlen und gibt es an den Benutzer zurück:

    EXECUTE BLOCK (x DOUBLE PRECISION = ?, y DOUBLE PRECISION = ?)
    RETURNS (gmean DOUBLE PRECISION)
    AS
    BEGIN
      gmean = SQRT(x*y);
      SUSPEND;
    END

    Da dieser Block Eingangsparameter hat, muss er zuerst vorbereitet werden.Anschließend können die Parameter eingestellt und der Block ausgeführt werden.Es hängt von der Client-Software ab, wie dies zu tun ist und ob es überhaupt möglich ist – siehe die Hinweise unten.

  3. Unser letztes Beispiel nimmt zwei ganzzahlige Werte an, kleinste und größte.Für alle Zahlen im Bereich kleinste…​größte gibt der Block die Zahl selbst, ihr Quadrat, ihren Kubus und ihre vierte Potenz aus.

    EXECUTE BLOCK (smallest INT = ?, largest INT = ?)
    RETURNS (number INT, square BIGINT, cube BIGINT, fourth BIGINT)
    AS
    BEGIN
      number = smallest;
      WHILE (number <= largest) DO
      BEGIN
        square = number * number;
        cube   = number * square;
        fourth = number * cube;
        SUSPEND;
        number = number + 1;
      END
    END

    Auch hier hängt es von der Client-Software ab, ob und wie Sie die Parameterwerte einstellen können.

Eingabe- und Ausgabeparameter

Die Ausführung eines Blocks ohne Eingabeparameter sollte mit jedem Firebird-Client möglich sein, der es dem Benutzer erlaubt, eigene DSQL-Anweisungen einzugeben.Wenn es Eingabeparameter gibt, wird es schwieriger: Diese Parameter müssen ihre Werte erhalten, nachdem die Anweisung vorbereitet wurde, aber bevor sie ausgeführt wird.Dies erfordert besondere Vorkehrungen, die nicht jede Client-Anwendung bietet.(Firebirds eigenes isql zum Beispiel nicht.)

Der Server akzeptiert nur Fragezeichen (“?”) als Platzhalter für die Eingabewerte, nicht “:a”, “:MyParam” etc., oder wörtliche Werte.Client-Software unterstützt jedoch möglicherweise das Formular “:xxx” und wird es vorverarbeiten, bevor es an den Server gesendet wird.

Wenn der Block Ausgangsparameter hat, muss Sie SUSPEND verwenden, sonst wird nichts zurückgegeben.

Die Ausgabe wird immer in Form einer Ergebnismenge zurückgegeben, genau wie bei einer SELECT-Anweisung.Sie können RETURNING_VALUES nicht verwenden oder den Block INTO einige Variablen ausführen, selbst wenn es nur eine Ergebniszeile gibt.

PSQL-Links

Weitere Informationen zum Schreiben von PSQL finden Sie in Kapitel Procedural SQL (PSQL)-Anweisungen.

Statement-Terminatoren

Einige Editoren für SQL-Anweisungen – insbesondere das Dienstprogramm isql, das mit Firebird geliefert wird, und möglicherweise einige Editoren von Drittanbietern – verwenden eine interne Konvention, die erfordert, dass alle Anweisungen mit einem Semikolon abgeschlossen werden.Dies führt beim Codieren in diesen Umgebungen zu einem Konflikt mit der PSQL-Syntax.Wenn Sie dieses Problem und seine Lösung nicht kennen, lesen Sie bitte die Details im PSQL-Kapitel im Abschnitt Terminator in isql umschalten.