

 Amazon Redshift unterstützt UDFs ab Patch 198 nicht mehr die Erstellung von neuem Python. Das bestehende Python UDFs wird bis zum 30. Juni 2026 weiterhin funktionieren. Weitere Informationen finden Sie im [Blog-Posting](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/). 

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Erstellen von gespeicherten Prozeduren in Amazon Redshift
<a name="stored-procedure-overview"></a>

In diesem Thema wird beschrieben, wie Sie in Amazon Redshift gespeicherte Prozeduren erstellen und verwenden. Eine gespeicherte Prozedur ist eine Sammlung von SQL-Anweisungen, die mehrere Programme verwenden können.

Sie können eine in Amazon Redshift gespeicherte Prozedur mit der PostgreSQL-Prozedursprache PL/pgSQL definieren, um eine Reihe von SQL-Abfragen und logischen Operationen durchzuführen. Die Prozedur wird in der Datenbank gespeichert und steht allen Benutzern mit ausreichenden Datenbankrechten zur Verfügung. 

Im Gegensatz zu einer benutzerdefinierten Funktion (UDF) kann eine gespeicherte Prozedur neben SELECT-Abfragen auch Data Definition Language (DDL) und Data Manipulation Language (DML) beinhalten. Eine gespeicherte Prozedur muss keinen Wert zurückgeben. Sie können prozedurale Sprache, einschließlich Schleifen und bedingte Ausdrücke, verwenden, um den logischen Ablauf zu steuern. 

Weitere Informationen zu SQL-Befehlen zum Erstellen und Verwalten von gespeicherten Prozeduren finden Sie in den folgenden Themen:
+ [CREATE PROCEDURE](r_CREATE_PROCEDURE.md)
+ [ALTER PROCEDURE](r_ALTER_PROCEDURE.md)
+ [DROP PROCEDURE](r_DROP_PROCEDURE.md)
+ [SHOW PROCEDURE](r_SHOW_PROCEDURE.md)
+ [CALL](r_CALL_procedure.md)
+ [GRANT](r_GRANT.md)
+ [REVOKE](r_REVOKE.md)
+ [ALTER DEFAULT PRIVILEGES](r_ALTER_DEFAULT_PRIVILEGES.md)

**Topics**
+ [Übersicht über gespeicherte Prozeduren in Amazon Redshift](stored-procedure-create.md)
+ [PL/pgSQL-Sprachreferenz](c_pl_pgSQL_reference.md)

# Übersicht über gespeicherte Prozeduren in Amazon Redshift
<a name="stored-procedure-create"></a>

In diesem Thema werden Einzelheiten zum Zweck und zur Verwendung von gespeicherten Prozeduren beschrieben.

Gespeicherte Prozeduren werden üblicherweise verwendet, um die Logik für Datentransformation, Datenvalidierung und die geschäftsspezifische Logik zu verkapseln. Durch das Zusammenfassen mehrerer SQL-Schritte in eine gespeicherte Prozedur können Sie die Austauschläufe zwischen Ihren Anwendungen und der Datenbank reduzieren.

Für eine fein abgestimmte Zugriffskontrolle können Sie zur Ausführung von Funktionen gespeicherte Prozeduren erstellen, ohne einem Benutzer Zugriff auf die zugrunde liegenden Tabellen zu gewähren. So kann beispielsweise nur der Eigentümer oder ein Superuser eine Tabelle kürzen, während ein Benutzer Schreibrechte benötigt, um Daten in eine Tabelle einzufügen. Anstatt einem Benutzer Rechte für die zugrunde liegenden Tabellen zu erteilen, können Sie eine gespeicherte Prozedur erstellen, die die Aufgabe ausführt. Anschließend erteilen Sie dem Benutzer Rechte zum Ausführen der gespeicherten Prozedur. 

Eine gespeicherte Prozedur mit dem Sicherheitsattribut DEFINER wird mit den Rechten des Eigentümers der gespeicherten Prozedur ausgeführt. Standardmäßig hat eine gespeicherte Prozedur die INVOKER-Sicherheit, d. h. die Prozedur verwendet die Rechte des Benutzers, der die Prozedur aufruft. 

Verwenden Sie den [CREATE PROCEDURE](r_CREATE_PROCEDURE.md)-Befehl zum Erstellen einer gespeicherten Prozedur. Verwenden Sie den [CALL](r_CALL_procedure.md)-Befehl zum Ausführen einer Prozedur. Beispiele dazu folgen später in diesem Abschnitt.

**Anmerkung**  
Einige Clients zeigen beim Erstellen einer in Amazon Redshift gespeicherten Prozedur möglicherweise den folgenden Fehler an.  

```
ERROR: 42601: [Amazon](500310) unterminated dollar-quoted string at or near "$$
```
Dieser Fehler tritt auf, wenn der Client die CREATE PROCEDURE-Anweisung mit Semikolons zur Trennung von Anweisungen und dem Dollarzeichen (\$1) für Zitate nicht korrekt parsen kann. Dies führt dazu, dass nur ein Teil der Anweisung an den Amazon-Redshift-Server gesendet wird. Dieser Fehler kann oftmals umgangen werden, indem die `Run as batch`- oder die `Execute selected`-Option des Clients verwendet wird.   
Verwenden Sie beispielsweise mit dem Aginity-Client die Option `Run entire script as batch`. Wenn Sie SQL Workbench/J, we recommend version 124. When you use SQL Workbench/J Version 125 verwenden, sollten Sie als Problemumgehung die Angabe eines alternativen Trennzeichens in Betracht ziehen.   
CREATE PROCEDURE enthält SQL-Anweisungen mit einem Semikolon (;). Wenn ein anderes Trennzeichen, wie ein Schrägstrich (/), festgelegt und am Ende der CREATE PROCEDURE-Anweisung platziert wird, wird die Anweisung zur Verarbeitung an den Amazon-Redshift-Server gesendet. Im Folgenden sehen Sie ein Beispiel.  

```
CREATE OR REPLACE PROCEDURE test()
AS $$
BEGIN
  SELECT 1 a;
END;
$$
LANGUAGE plpgsql
;
/
```
Sie können einen Client verwenden, der das Parsen von CREATE PROCEDURE-Anweisungen unterstützt, z. B. den [Abfrage-Editor in der Amazon Redshift Redshift-Konsole](https://docs.aws.amazon.com/redshift/latest/mgmt/query-editor.html) oder. TablePlus 

**Topics**
+ [Benennen von gespeicherten Prozeduren](stored-procedure-naming.md)
+ [Sicherheit und Berechtigungen für gespeicherte Prozeduren](stored-procedure-security-and-privileges.md)
+ [Rückgabe eines Ergebnissatzes von einer gespeicherten Prozedur](stored-procedure-result-set.md)
+ [Verwalten von Transaktionen](stored-procedure-transaction-management.md)
+ [Aufspüren von Fehlern](stored-procedure-trapping-errors.md)
+ [Protokollieren von gespeicherten Prozeduren](c_PLpgSQL-logging.md)
+ [Einschränkungen bei gespeicherten Prozeduren](stored-procedure-constraints.md)

Das folgende Beispiel zeigt eine Prozedur ohne Ausgabeparameter. Standardmäßig handelt es sich bei Parametern um Eingabe-(IN)-Parameter.

```
CREATE OR REPLACE PROCEDURE test_sp1(f1 int, f2 varchar)
AS $$
BEGIN
  RAISE INFO 'f1 = %, f2 = %', f1, f2;
END;
$$ LANGUAGE plpgsql;

call test_sp1(5, 'abc');
INFO: f1 = 5, f2 = abc
CALL
```



**Anmerkung**  
 Wenn Sie gespeicherte Prozeduren schreiben, empfehlen wir eine bewährte Methode zum Sichern sensibler Werte:   
Nehmen Sie keine Hartkodierung für sensible Informationen in der Logik der gespeicherten Prozedur vor.hardco Weisen Sie beispielsweise kein Benutzerkennwort in einer CREATE USER-Anweisung im Text einer gespeicherten Prozedur zu. Dies stellt ein Sicherheitsrisiko dar, da hartkodierte Werte als Schema-Metadaten in Katalogtabellen aufgezeichnet werden können. Übergeben Sie stattdessen mithilfe von Parametern sensible Werte wie Passwörter als Argumente an die gespeicherte Prozedur.   
Weitere Informationen zu gespeicherten Prozeduren finden Sie unter [CREATE PROCEDURE](r_CREATE_PROCEDURE.md) und [Erstellen von gespeicherten Prozeduren in Amazon Redshift](stored-procedure-overview.md). Weitere Informationen zu Katalogtabellen finden Sie unter [Systemkatalogtabellen](c_intro_catalog_views.md).

Das folgende Beispiel zeigt eine Prozedur mit Ausgabeparametern. Es gibt Eingabe (IN)-, Eingabe und Ausgabe (INOUT)- sowie Ausgabe (OUT)-Parameter.

```
CREATE OR REPLACE PROCEDURE test_sp2(f1 IN int, f2 INOUT varchar(256), out_var OUT varchar(256))
AS $$
DECLARE
  loop_var int;
BEGIN
  IF f1 is null OR f2 is null THEN
    RAISE EXCEPTION 'input cannot be null';
  END IF;
  DROP TABLE if exists my_etl;
  CREATE TEMP TABLE my_etl(a int, b varchar);
    FOR loop_var IN 1..f1 LOOP
        insert into my_etl values (loop_var, f2);
        f2 := f2 || '+' || f2;
    END LOOP;
  SELECT INTO out_var count(*) from my_etl;
END;
$$ LANGUAGE plpgsql;


call test_sp2(2,'2019');

         f2          | column2
---------------------+---------
 2019+2019+2019+2019 | 2
(1 row)
```

# Benennen von gespeicherten Prozeduren
<a name="stored-procedure-naming"></a>

In diesem Thema werden Einzelheiten zu den Namen gespeicherter Prozeduren beschrieben.

Wenn Sie eine Prozedur mit dem gleichen Namen und verschiedenen Datentypen von Eingabeparametern oder einer Signatur definieren, erstellen Sie eine neue Prozedur. Infolgedessen ist der Prozedurname überladen. Weitere Informationen finden Sie unter [Überladen von Prozedurnamen](#stored-procedure-overloading-name). Amazon Redshift lässt keine Prozedurenüberladung aufgrund von Ausgabeparametern zu. Sie können nicht zwei Prozeduren mit dem gleichen Namen und den gleichen Datentypen von Eingabeparametern, jedoch unterschiedlichen Typen von Ausgabeparametern haben.

Der Eigentümer oder ein Superuser kann den Inhalt einer gespeicherten Prozedur durch einen neuen mit derselben Signatur ersetzen. Zum Ändern der Signatur oder des Rückgabetyps einer gespeicherten Prozedur, lassen Sie die gespeicherte Prozedur fallen und erstellen sie neu. Weitere Informationen erhalten Sie unter [DROP PROCEDURE](r_DROP_PROCEDURE.md) und [CREATE PROCEDURE](r_CREATE_PROCEDURE.md).

Sie können mögliche Namenskonflikte und unerwartete Ergebnisse verhindern, wenn Sie sich bei der Implementierung an Ihre Namenskonventionen halten. Da Prozedurnamen überladen werden können, kann es Konflikte mit existierenden und zukünftigen Amazon-Redshift-Prozedurnamen geben.

## Überladen von Prozedurnamen
<a name="stored-procedure-overloading-name"></a>

Eine Prozedur wird anhand Ihres Namens und ihrer Signatur identifiziert, wobei die Signatur die Anzahl und die Datentypen der Eingabeparameter ist. Solange sich zwei Funktionen in demselben Schema bezüglich ihrer Signatur unterscheiden, können sie denselben Namen haben. Mit anderen Worten: Sie können Prozedurnamen überladen.

Wenn Sie eine Prozedur ausführen, bestimmt die Abfrage-Engine anhand der Anzahl der von Ihnen angegebenen Parameter und ihrer Datentypen, welches Verfahren aufgerufen werden soll. Sie können das Überladen nutzen, um Prozeduren mit einer variablen Anzahl von Parametern bis zu der im Befehl CREATE PROCEDURE erlaubten Grenze stimulieren. Weitere Informationen finden Sie unter [CREATE PROCEDURE](r_CREATE_PROCEDURE.md).

## Verhindern von Namenskonflikten
<a name="stored-procedure-name-conflicts"></a>

Es wird empfohlen, dass Sie alle Prozeduren mit dem Präfix `sp_` benennen. Amazon Redshift reserviert das Präfix `sp_` ausschließlich für gespeicherte Prozeduren. Indem Sie Ihren Prozedurnamen das Präfix `sp_` hinzufügen, stellen Sie sicher, dass Ihr Prozedurname keine Konflikte mit vorhandenen oder zukünftigen Amazon-Redshift-Prozedurnamen auslöst. 

# Sicherheit und Berechtigungen für gespeicherte Prozeduren
<a name="stored-procedure-security-and-privileges"></a>

In diesem Thema werden die Datenbank-Anmeldeinformationen beschrieben, die zum Erstellen und Ausführen von gespeicherten Prozeduren erforderlich sind.

Standardmäßig sind alle Benutzer zum Erstellen einer Prozedur berechtigt. Um eine Prozedur zu erstellen, benötigen Sie das USAGE-Privileg für die Sprache PL/pgSQL, which is granted to PUBLIC by default. Only superusers and owners have the privilege to call a procedure by default. Superusers can run REVOKE USAGE on PL/pgSQL eines Benutzers, wenn dieser verhindern möchte, dass der Benutzer eine gespeicherte Prozedur erstellt. 

Damit Sie eine Prozedur aufrufen können, muss Ihnen das EXECUTE-Recht für die Prozedur erteilt werden. Standardmäßig wird dem Eigentümer der Prozedur und Superusern das EXECUTE-Recht für neue Prozeduren erteilt. Weitere Informationen finden Sie unter [GRANT](r_GRANT.md). 

Der Benutzer, der eine Prozedur erstellt hat, ist standardmäßig der Eigentümer. Standardmäßig hat der Eigentümer für die Prozedur folgende Berechtigungen: CREATE, DROP und EXECUTE. Superuser verfügen über alle Berechtigungen. 

Das SECURITY-Attribut steuert die Berechtigungen einer Prozedur für den Zugriff auf Datenbankobjekte. Wenn Sie eine gespeicherte Prozedur erstellen, können Sie das SECURITY-Attribut entweder auf DEFINER oder auf INVOKER einstellen. Dieses Attribut bestimmt, welche Rechte bei der Ausführung der Anweisungen im Hauptteil der gespeicherten Prozedur verwendet werden. Wenn Sie den SECURTIY INVOKER bestimmen, verwendet die Prozedur die Berechtigungen des Benutzers, der die Prozedur aufruft. Wenn Sie den SECURITY DEFINER bestimmen, verwendet die Prozedur die Berechtigungen des Eigentümers der Prozedur. INVOKER ist die Standardeinstellung. 

Da eine SECURITY-DEFINER-Prozedur mit den Rechten des Eigentümers ausgeführt wird, müssen Sie sicherstellen, dass die Prozedur nicht missbraucht werden kann. Um sicherzustellen, dass SECURITY-DEFINER-Prozeduren nicht missbraucht werden können, sollten Sie Folgendes tun:
+ Gewähren Sie die Ausführung von SECURITY DEFINER-Prozeduren nur bestimmten Benutzern und nicht der Öffentlichkeit.
+ Qualifizieren Sie alle Datenbankobjekte, auf die die Prozedur zugreifen muss, mit Schema-Namen. Verwenden Sie z. B. `myschema.mytable` anstatt nur `mytable` zu verwenden.
+ Wenn Sie einen Objektnamen nicht anhand seines Schemas qualifizieren können, legen Sie `search_path` beim Erstellen der Prozedur mit der Option „SET“ fest.- Legen Sie `search_path` fest, um alle Schemata auszuschließen, die von nicht vertrauenswürdigen Benutzern beschreibbar sind. Dieser Ansatz verhindert, dass Aufrufer dieser Prozedur Objekte (z. B. Tabellen oder Ansichten) anlegen, die Objekte maskieren, die für die Verwendung durch die Prozedur vorgesehen sind. Weitere Informationen zur Option „SET“ finden Sie unter [CREATE PROCEDURE](r_CREATE_PROCEDURE.md). 

Das folgende Beispiel legt `search_path` auf `admin` fest, um zu gewährleisten, dass auf die `user_creds`-Tabelle über das `admin`-Schema zugegriffen werden kann und nicht aus der Öffentlichkeit oder über irgendein anderes Schema im `search_path` des Aufrufers.

```
CREATE OR REPLACE PROCEDURE sp_get_credentials(userid int, o_creds OUT varchar)
AS $$
BEGIN
  SELECT creds INTO o_creds
  FROM user_creds
  WHERE user_id = $1;
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER
-- Set a secure search_path
SET search_path = admin;
```

# Rückgabe eines Ergebnissatzes von einer gespeicherten Prozedur
<a name="stored-procedure-result-set"></a>

In diesem Thema wird beschrieben, wie gespeicherte Prozeduren Daten zurückgeben.

Sie können eine Ergebnismenge zurückgeben unter Verwendung eines Cursors oder einer temporären Tabelle.

## Rückgabe eines Cursors
<a name="stored-procedure-return-cursor"></a>

Erstellen Sie für die Rückgabe eines Cursors eine Prozedur mit einem INOUT-Argument, das mit einem `refcursor`-Datentyp definiert ist. Wenn Sie die Prozedur aufrufen, benennen Sie den Cursor. Dann können Sie die Ergebnisse anhand des Namens vom Cursor abrufen.

Das folgende Beispiel erstellt eine Prozedur mit Namen `get_result_set` mit einem INOUT-Argument mit Namen `rs_out`. Sie verwendet den `refcursor`-Datentyp. Die Prozedur öffnet den Cursor mit einer SELECT-Anweisung.

```
CREATE OR REPLACE PROCEDURE get_result_set (param IN integer, rs_out INOUT refcursor)
AS $$
BEGIN
  OPEN rs_out FOR SELECT * FROM fact_tbl where id >= param;
END;
$$ LANGUAGE plpgsql;
```

Der folgende AUFRUF-Befehl öffnet den Cursor mit dem Namen `mycursor`. Verwenden Sie Cursor nur während Transaktionen. 

```
BEGIN;
CALL get_result_set(1, 'mycursor');
```

Wenn der Cursor geöffnet ist, können Sie aus dem Cursor abrufen, wie das folgende Beispiel zeigt.

```
FETCH ALL FROM mycursor;

    id | secondary_id | name
-------+--------------+---------
     1 |            1 | Joe
     1 |            2 | Ed
     2 |            1 | Mary
     1 |            3 | Mike
(4 rows)
```

Schließlich wird die Transaktion entweder bestätigt oder zurückgesetzt.

```
COMMIT;   
```

Ein von einer gespeicherten Prozedur zurückgegebener Cursor unterliegt denselben Einschränkungen und Leistungsüberlegungen, wie in DECLARE CURSOR beschrieben. Weitere Informationen finden Sie unter [Einschränkungen für Cursors](declare.md#declare-constraints).

Das folgende Beispiel zeigt das Aufrufen der `get_result_set` gespeicherten Prozedur unter Verwendung eines `refcursor`-Datentyps von JDBC. Der wortgetreue `'mycursor'` (der Cursorname) wird an den `prepareStatement` übergeben. Anschließend werden die Ergebnisse von dem aufgerufen `ResultSet`.

```
static void refcursor_example(Connection conn) throws SQLException {
    conn.setAutoCommit(false);
    PreparedStatement proc = conn.prepareStatement("CALL get_result_set(1, 'mycursor')");
    proc.execute();
    ResultSet rs = statement.executeQuery("fetch all from mycursor");
    while (rs.next()) {
      int n = rs.getInt(1);
      System.out.println("n " + n);
    }
```

## Verwenden einer temporären Tabelle
<a name="stored-procedure-return-cursor"></a>

Um Ergebnisse zu erhalten, können Sie ein Kürzel einer temporären Tabelle zurückgeben, die Ergebniszeilen enthält. Der Client kann der gespeicherten Prozedur als Parameter einen Namen zuweisen. Innerhalb der gespeicherten Prozedur kann Dynamic SQL verwendet werden, um die temporäre Tabelle zu bearbeiten. Es folgt ein Beispiel.

```
CREATE PROCEDURE get_result_set(param IN integer, tmp_name INOUT varchar(256)) as $$
DECLARE
  row record;
BEGIN
  EXECUTE 'drop table if exists ' || tmp_name;
  EXECUTE 'create temp table ' || tmp_name || ' as select * from fact_tbl where id <= ' || param;
END;
$$ LANGUAGE plpgsql;

CALL get_result_set(2, 'myresult');
 tmp_name
-----------
 myresult
(1 row)

SELECT * from myresult;
 id | secondary_id | name
----+--------------+------
  1 |            1 | Joe
  2 |            1 | Mary
  1 |            2 | Ed
  1 |            3 | Mike
(4 rows)
```

# Verwalten von Transaktionen
<a name="stored-procedure-transaction-management"></a>

Sie können eine gespeicherte Prozedur mit standardmäßigem Transaktionsverwaltungsverhalten oder nichtatomarem Verhalten erstellen. 

## Standardmodus der Transaktionsverwaltung für gespeicherte Prozeduren
<a name="stored-procedure-transaction-management-default-mode"></a>

Die standardmäßige Verhalten der automatischen Commits im Transaktionsmodus bewirkt, das jeder SQL-Befehl, der separat ausgeführt wird, einzeln übernommen wird. Der Aufruf einer gespeicherten Prozedur wird wie ein einzelner SQL-Befehl behandelt. Die SQL-Anweisungen innerhalb einer Prozedur verhalten sich so, als ob sie sich in einem Transaktionsblock befinden, der implizit mit dem Start des Aufrufs beginnt und mit dem Ende des Aufrufs endet. Ein verschachtelter Aufruf einer anderen Prozedur wird wie jede andere SQL-Anweisung behandelt und arbeitet im Kontext derselben Transaktion wie der Aufrufer. Weitere Informationen über automatisches Commit-Verhalten finden Sie unter [Isolierungsstufen in Amazon Redshift](c_serial_isolation.md).

Nehmen wir jedoch an, Sie rufen eine gespeicherte Prozedur aus einem von einem Benutzer angegebenen Transaktionsblock auf (definiert durch BEGIN...COMMIT). In diesem Fall werden alle Anweisungen in der gespeicherten Prozedur im Kontext der vom Benutzer angegebenen Transaktion ausgeführt. Die Prozedur wird beim Beenden nicht implizit übergeben. Der Anrufer steuert die Commit- oder Rollback-Prozedur.

Tritt beim Ausführen einer gespeicherten Prozedur ein Fehler auf, werden alle in der aktuellen Transaktion vorgenommenen Änderungen zurückgesetzt.

Sie können die folgenden Transaktionskontroll-Anweisungen in einer gespeicherten Prozedur verwenden:
+ COMMIT – Sendet alle erledigten Aufgaben in der aktuellen Transaktion und beginnt implizit eine neue Transaktion. Weitere Informationen finden Sie unter [COMMIT](r_COMMIT.md). 
+ ROLLBACK – Setzt die erledigten Aufgaben in der aktuellen Transaktion zurück und beginnt implizit eine neue Transaktion. Weitere Informationen finden Sie unter [ROLLBACK](r_ROLLBACK.md). 

TRUNCATE ist eine weitere Anweisung, die Sie von innerhalb einer gespeicherten Prozedur aus zur Transaktionsverwaltung verwenden können. In Amazon Redshift gibt TRUNCATE implizit einen Commit aus. Dieses Verhalten wird auch im Kontext von gespeicherten Prozeduren beibehalten. Wird eine TRUNCATE-Anweisung innerhalb einer gespeicherten Prozedur ausgegeben, überträgt sie die aktuelle Transaktion und beginnt eine neue. Weitere Informationen finden Sie unter [TRUNCATE](r_TRUNCATE.md). 

Alle Anweisungen, die einer COMMIT-, ROLLBACK- oder TRUNCATE-Anweisung folgen, werden im Kontext einer neuen Transaktion ausgeführt. Dies geschieht so lange, bis eine COMMIT-, ROLLBACK- oder TRUNCATE-Anweisung aufgerufen oder die gespeicherte Prozedur beendet wird.

Wenn Sie eine COMMIT-, ROLLBACK- oder TRUNCATE-Anweisung aus einer gespeicherten Prozedur heraus verwenden, gelten die folgenden Einschränkungen:
+ Wenn die gespeicherte Prozedur innerhalb eines Transaktionsblocks aufgerufen wird, kann sie keine COMMIT-, ROLLBACK- oder TRUNCATE-Anweisung ausgeben. Diese Einschränkung gilt innerhalb des eigenen Hauptteils der gespeicherten Prozedur sowie innerhalb aller verschachtelten Prozeduraufrufe.
+ Wenn die gespeicherte Prozedur mit `SET config`-Optionen erstellt wird, kann sie keine COMMIT-, ROLLBACK- oder TRUNCATE-Anweisung ausgeben. Diese Einschränkung gilt innerhalb des eigenen Hauptteils der gespeicherten Prozedur sowie innerhalb aller verschachtelten Prozeduraufrufe. 
+ Jeder offene Cursor (explizit oder implizit) wird automatisch geschlossen, wenn eine COMMIT-, ROLLBACK- oder TRUNCATE-Anweisung verarbeitet wird. Einschränkungen für explizite oder implizite Cursor finden Sie unter [Einschränkungen bei gespeicherten Prozeduren](stored-procedure-constraints.md).

Darüber hinaus können Sie mit dynamischen SQL keine COMMIT- oder ROLLBACK-Anweisungen ausführen. TRUNCATE-Anweisungen können jedoch mit dynamischem SQL ausgeführt werden. Weitere Informationen finden Sie unter [Dynamisches SQL](c_PLpgSQL-statements.md#r_PLpgSQL-dynamic-sql). 

Beachten Sie bei der Arbeit mit gespeicherten Prozeduren, dass die BEGIN- und END-Anweisungen in PL/pgSQL nur der Gruppierung dienen. Sie beginnen weder eine Transaktion, noch beenden sie eine. Weitere Informationen finden Sie unter [Block](c_PLpgSQL-structure.md#r_PLpgSQL-block). 

Das folgende Beispiel zeigt das Transaktionsverhalten beim Aufruf einer gespeicherten Prozedur aus einem explizitenTransaktionsblock heraus. Die beiden Insert-Anweisungen, die von außerhalb der gespeicherten Prozedur sowie aus ihr heraus ausgegeben werden, sind alle Teil derselben Transaktion (3382). Die Transaktion wird bestätigt, wenn der Benutzer den expliziten Commit durchführt.

```
CREATE OR REPLACE PROCEDURE sp_insert_table_a(a int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO test_table_a values (a);
END;
$$;

Begin;
  insert into test_table_a values (1);
  Call sp_insert_table_a(2);
  insert into test_table_a values (3);
Commit; 

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  | pid |  type   |               stmt_text
--------+------+-----+---------+----------------------------------------
    103 | 3382 | 599 | UTILITY | Begin;
    103 | 3382 | 599 | QUERY   | insert into test_table_a values (1);
    103 | 3382 | 599 | UTILITY | Call sp_insert_table_a(2);
    103 | 3382 | 599 | QUERY   | INSERT INTO test_table_a values ( $1 )
    103 | 3382 | 599 | QUERY   | insert into test_table_a values (3);
    103 | 3382 | 599 | UTILITY | COMMIT
```

Sehen wir uns im Gegenzug folgendes Beispiel an: Wenn dieselben Anweisungen von außerhalb eines expliziten Transaktionsblocks ausgegeben werden, und Autocommit der Sitzung auf ON gesetzt ist, wird jede Anweisung in einer eigenen Transaktion ausgeführt.

```
insert into test_table_a values (1);
Call sp_insert_table_a(2);
insert into test_table_a values (3);

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  | pid |  type   |                                                                    stmt_text
--------+------+-----+---------+-------------------------------------------------------------------------------------------------------------------------------------------------
    103 | 3388 | 599 | QUERY   | insert into test_table_a values (1);
    103 | 3388 | 599 | UTILITY | COMMIT
    103 | 3389 | 599 | UTILITY | Call sp_insert_table_a(2);
    103 | 3389 | 599 | QUERY   | INSERT INTO test_table_a values ( $1 )
    103 | 3389 | 599 | UTILITY | COMMIT
    103 | 3390 | 599 | QUERY   | insert into test_table_a values (3);
    103 | 3390 | 599 | UTILITY | COMMIT
```

Das folgende Beispiel gibt eine TRUNCATE-Anweisung heraus, nach dem Einfügen in `test_table_a`. Die TRUNCATE-Anweisung gibt einen impliziten Commit heraus, der die derzeitige Transaktion (3335) bestätigt und eine neue startet (3336). Die neue Transaktion wird beim Beenden der Prozedur bestätigt.

```
CREATE OR REPLACE PROCEDURE sp_truncate_proc(a int, b int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO test_table_a values (a);
  TRUNCATE test_table_b;
  INSERT INTO test_table_b values (b);
END;
$$;

Call sp_truncate_proc(1,2);

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |  pid  |  type   |                                                                                             stmt_text
--------+------+-------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    103 | 3335 | 23636 | UTILITY | Call sp_truncate_proc(1,2);
    103 | 3335 | 23636 | QUERY   | INSERT INTO test_table_a values ( $1 )
    103 | 3335 | 23636 | UTILITY | TRUNCATE test_table_b
    103 | 3335 | 23636 | UTILITY | COMMIT
    103 | 3336 | 23636 | QUERY   | INSERT INTO test_table_b values ( $1 )
    103 | 3336 | 23636 | UTILITY | COMMIT
```

Das folgende Beispiel gibt ein TRUNCATE aus einem verschachtelten Aufruf heraus. TRUNCATE bestätigt die bisher geleistete Arbeit in den äußeren und inneren Prozeduren einer Transaktion (3344). Es startet eine neue Transaktion (3345). Die neue Transaktion wird beim Beenden der äußeren Prozedur bestätigt.

```
CREATE OR REPLACE PROCEDURE sp_inner(c int, d int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO inner_table values (c);
  TRUNCATE outer_table;
  INSERT INTO inner_table values (d);
END;
$$;

CREATE OR REPLACE PROCEDURE sp_outer(a int, b int, c int, d int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO outer_table values (a);
  Call sp_inner(c, d);
  INSERT INTO outer_table values (b);
END;
$$;

Call sp_outer(1, 2, 3, 4);

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |  pid  |  type   |                                                                                              stmt_text
--------+------+-------+---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    103 | 3344 | 23636 | UTILITY | Call sp_outer(1, 2, 3, 4);
    103 | 3344 | 23636 | QUERY   | INSERT INTO outer_table values ( $1 )
    103 | 3344 | 23636 | UTILITY | CALL sp_inner( $1 , $2 )
    103 | 3344 | 23636 | QUERY   | INSERT INTO inner_table values ( $1 )
    103 | 3344 | 23636 | UTILITY | TRUNCATE outer_table
    103 | 3344 | 23636 | UTILITY | COMMIT
    103 | 3345 | 23636 | QUERY   | INSERT INTO inner_table values ( $1 )
    103 | 3345 | 23636 | QUERY   | INSERT INTO outer_table values ( $1 )
    103 | 3345 | 23636 | UTILITY | COMMIT
```

Das folgende Beispiel zeigt, dass Cursor `cur1` geschlossen wurde, als die TRUNCATE-Anweisung durchgeführt wurde.

```
CREATE OR REPLACE PROCEDURE sp_open_cursor_truncate()
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
  cur1 cursor for select * from test_table_a order by 1;
BEGIN
  open cur1;
  TRUNCATE table test_table_b;
  Loop
    fetch cur1 into rec;
    raise info '%', rec.c1;
    exit when not found;
  End Loop;
END
$$;

call sp_open_cursor_truncate();
ERROR: cursor "cur1" does not exist
CONTEXT: PL/pgSQL function "sp_open_cursor_truncate" line 8 at fetch
```

Im folgenden Beispiel wird eine TRUNCATE-Anweisung herausgegeben und kann nicht aus einem expliziten Transaktionsblock heraus aufgerufen werden.

```
CREATE OR REPLACE PROCEDURE sp_truncate_atomic() LANGUAGE plpgsql
AS $$
BEGIN
  TRUNCATE test_table_b;
END;
$$;

Begin;
  Call sp_truncate_atomic();
ERROR: TRUNCATE cannot be invoked from a procedure that is executing in an atomic context.
HINT: Try calling the procedure as a top-level call i.e. not from within an explicit transaction block. 
Or, if this procedure (or one of its ancestors in the call chain) was created with SET config options, recreate the procedure without them.
CONTEXT: SQL statement "TRUNCATE test_table_b"
PL/pgSQL function "sp_truncate_atomic" line 2 at SQL statement
```

Das folgende Beispiel zeigt, dass ein Benutzer, der kein Superuser oder Besitzer einer Tabelle ist, eine TRUNCATE-Anweisung für die Tabelle ausgeben kann. Der Benutzer verwendet dafür eine `Security Definer`-gespeicherte Prozedur. Das Beispiel zeigt die folgenden Aktionen: 
+ Der Benutzer1 erstellt eine Tabelle `test_tbl`. 
+ Der Benutzer1 erstellt die gespeicherte Prozedur `sp_truncate_test_tbl`. 
+ Der Benutzer1 erteilt Benutzer2 die Berechtigung `EXECUTE` für die gespeicherte Prozedur. 
+ Der Benutzer2 führt die gespeicherte Prozedur aus, um Tabelle verkürzen `test_tbl`. Das Beispiel zeigt die Zeilenanzahl vor und nach dem Befehl `TRUNCATE`. 

```
set session_authorization to user1;
create table test_tbl(id int, name varchar(20));
insert into test_tbl values (1,'john'), (2, 'mary');
CREATE OR REPLACE PROCEDURE sp_truncate_test_tbl() LANGUAGE plpgsql
AS $$
DECLARE
  tbl_rows int;
BEGIN
  select count(*) into tbl_rows from test_tbl;
  RAISE INFO 'RowCount before Truncate: %', tbl_rows;
  TRUNCATE test_tbl;
  select count(*) into tbl_rows from test_tbl;
  RAISE INFO 'RowCount after Truncate: %', tbl_rows;
END;
$$ SECURITY DEFINER;
grant execute on procedure sp_truncate_test_tbl() to user2;
reset session_authorization;


set session_authorization to user2;
call sp_truncate_test_tbl();
INFO:  RowCount before Truncate: 2
INFO:  RowCount after Truncate: 0
CALL
reset session_authorization;
```

Im folgenden Beispiel wird COMMIT zweimal ausgegeben. Mit der ersten COMMIT-Anweisung werden alle in der Transaktion 10363 erledigten Aufgaben gesendet und implizit die Transaktion 10364 gestartet. Transaktion 10364 wird über die zweite COMMIT-Anweisung gesendet. 

```
CREATE OR REPLACE PROCEDURE sp_commit(a int, b int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO test_table values (a);
  COMMIT;
  INSERT INTO test_table values (b);
  COMMIT;
END;
$$;

call sp_commit(1,2);

select userid, xid, pid, type, trim(text) as stmt_text
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;
 userid |  xid  | pid  |  type   |                                                                                    stmt_text
--------+-------+------+---------+-----------------------------------------------------------------------------------------------------------------
    100 | 10363 | 3089 | UTILITY | call sp_commit(1,2);
    100 | 10363 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10363 | 3089 | UTILITY | COMMIT
    100 | 10364 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10364 | 3089 | UTILITY | COMMIT
```

Im folgenden Beispiel wird eine ROLLBACK-Anweisung ausgegeben, wenn `sum_vals` größer als 2 ist. Die erste ROLLBACK-Anweisung setzt alle erledigten Aufgaben in Transaktion 10377 zurück und startet eine neue Transaktion 10378. Die Transaktion 10378 wird beim Beenden der Prozedur bestätigt. 

```
CREATE OR REPLACE PROCEDURE sp_rollback(a int, b int) LANGUAGE plpgsql
AS $$
DECLARE
  sum_vals int;
BEGIN
  INSERT INTO test_table values (a);
  SELECT sum(c1) into sum_vals from test_table;
  IF sum_vals > 2 THEN
    ROLLBACK;
  END IF;
  
  INSERT INTO test_table values (b);
END;
$$;

call sp_rollback(1, 2);

select userid, xid, pid, type, trim(text) as stmt_text
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

userid |  xid  | pid  |  type   |                                                                                    stmt_text
--------+-------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    100 | 10377 | 3089 | UTILITY | call sp_rollback(1, 2);
    100 | 10377 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10377 | 3089 | QUERY   | SELECT sum(c1) from test_table
    100 | 10377 | 3089 | QUERY   | Undoing 1 transactions on table 133646 with current xid 10377 : 10377
    100 | 10378 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10378 | 3089 | UTILITY | COMMIT
```

## Nichtatomarer Modus der Transaktionsverwaltung für gespeicherte Prozeduren
<a name="stored-procedure-transaction-management-nonatomic-mode"></a>

Eine gespeicherte Prozedur, die im Modus NONATOMIC erstellt wurde, hat ein anderes Transaktionskontrollverhalten als eine Prozedur, die im Standardmodus erstellt wurde. Ähnlich wie beim automatischen Commit-Verhalten von SQL-Befehlen außerhalb von gespeicherten Prozeduren wird jede SQL-Anweisung innerhalb einer NONATOMIC-Prozedur in einer eigenen Transaktion ausgeführt und automatisch übernommen. Wenn ein Benutzer einen expliziten Transaktionsblock innerhalb einer gespeicherten NONATOMIC-Prozedur beginnt, werden die SQL-Anweisungen innerhalb des Blocks nicht automatisch übernommen. Der Transaktionsblock steuert den Commit oder das Rollback der darin enthaltenen Anweisungen. 

In gespeicherten NONATOMIC-Prozeduren können Sie mithilfe der Anweisung START TRANSACTION einen expliziten Transaktionsblock innerhalb der Prozedur öffnen. Wenn es jedoch bereits einen offenen Transaktionsblock gibt, hat diese Anweisung keinerlei Auswirkungen, da Amazon Redshift Untertransaktionen nicht unterstützt. Die vorherige Transaktion wird fortgesetzt.

Wenn Sie mit FOR-Cursor-Schleifen innerhalb einer NONATOMIC-Prozedur arbeiten, stellen Sie sicher, dass Sie einen expliziten Transaktionsblock öffnen, bevor Sie die Ergebnisse einer Abfrage durchlaufen. Andernfalls wird der Cursor geschlossen, wenn die SQL-Anweisung innerhalb der Schleife automatisch übernommen wird.

Einige Überlegungen bei der Verwendung des Modus NONATOMIC lauten wie folgt:
+ Jede SQL-Anweisung innerhalb der gespeicherten Prozedur wird automatisch übernommen, wenn kein Transaktionsblock geöffnet und Autocommit für die Sitzung auf AN gesetzt ist.
+ Sie können eine COMMIT/ROLLBACK/TRUNCATE Anweisung zum Beenden der Transaktion ausgeben, wenn die gespeicherte Prozedur innerhalb eines Transaktionsblocks aufgerufen wird. Dies ist im Standardmodus nicht möglich.
+ Sie können die Anweisung START TRANSACTION ausgeben, um einen Transaktionsblock innerhalb der gespeicherten Prozedur zu starten.

Die folgenden Beispiele veranschaulichen das Transaktionsverhalten bei der Arbeit mit gespeicherten NONATOMIC Prozeduren. In der Sitzung für alle folgenden Beispiele ist Autocommit auf AN gesetzt.

Im folgenden Beispiel umfasst eine gespeicherte NONATOMIC-Prozedur zwei INSERT-Anweisungen. Wenn die Prozedur außerhalb eines Transaktionsblocks aufgerufen wird, wird jede INSERT-Anweisung innerhalb der Prozedur automatisch übernommen. 

```
CREATE TABLE test_table_a(v int); 
CREATE TABLE test_table_b(v int); 

CREATE OR REPLACE PROCEDURE sp_nonatomic_insert_table_a(a int, b int) NONATOMIC AS
$$
BEGIN
    INSERT INTO test_table_a values (a);
    INSERT INTO test_table_b values (b);
END;
$$ 
LANGUAGE plpgsql;

Call sp_nonatomic_insert_table_a(1,2);

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |               stmt_text                
--------+------+------------+---------+----------------------------------------
      1 | 1792 | 1073807554 | UTILITY | Call sp_nonatomic_insert_table_a(1,2);
      1 | 1792 | 1073807554 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1792 | 1073807554 | UTILITY | COMMIT
      1 | 1793 | 1073807554 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1793 | 1073807554 | UTILITY | COMMIT
(5 rows)
```

Wenn die Prozedur jedoch innerhalb eines BEGIN..COMMIT-Blocks aufgerufen wird, sind alle Anweisungen Teil derselben Transaktion (xid=1799). 

```
Begin;
  INSERT INTO test_table_a values (10);
  Call sp_nonatomic_insert_table_a(20,30);
  INSERT INTO test_table_b values (40);
Commit; 

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |                stmt_text                 
--------+------+------------+---------+------------------------------------------
      1 | 1799 | 1073914035 | UTILITY | Begin;
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_a values (10);
      1 | 1799 | 1073914035 | UTILITY | Call sp_nonatomic_insert_table_a(20,30);
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_b values (40);
      1 | 1799 | 1073914035 | UTILITY | COMMIT
(7 rows)
```

In diesem Beispiel befinden sich zwei INSERT-Anweisungen zwischen START TRANSACTION...COMMIT. Wenn die Prozedur außerhalb eines Transaktionsblocks aufgerufen wird, befinden sich die beiden INSERT-Anweisungen in derselben Transaktion (xid=1866). 

```
CREATE OR REPLACE PROCEDURE sp_nonatomic_txn_block(a int, b int) NONATOMIC AS
$$
BEGIN
    START TRANSACTION;
    INSERT INTO test_table_a values (a);
    INSERT INTO test_table_b values (b);
    COMMIT;
END;
$$ 
LANGUAGE plpgsql;

Call sp_nonatomic_txn_block(1,2);

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |               stmt_text                
--------+------+------------+---------+----------------------------------------
      1 | 1865 | 1073823998 | UTILITY | Call sp_nonatomic_txn_block(1,2);
      1 | 1866 | 1073823998 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1866 | 1073823998 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1866 | 1073823998 | UTILITY | COMMIT
(4 rows)
```

Wenn die Prozedur innerhalb eines BEGIN...COMMIT-Blocks aufgerufen wird, bewirkt START TRANSACTION innerhalb der Prozedur nichts, da bereits eine offene Transaktion vorhanden ist. Durch den COMMIT innerhalb der Prozedur wird die aktuelle Transaktion übernommen (xid=1876) und eine neue gestartet.

```
Begin;
  INSERT INTO test_table_a values (10);
  Call sp_nonatomic_txn_block(20,30);
  INSERT INTO test_table_b values (40);
Commit; 

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |               stmt_text                
--------+------+------------+---------+----------------------------------------
      1 | 1876 | 1073832133 | UTILITY | Begin;
      1 | 1876 | 1073832133 | QUERY   | INSERT INTO test_table_a values (10);
      1 | 1876 | 1073832133 | UTILITY | Call sp_nonatomic_txn_block(20,30);
      1 | 1876 | 1073832133 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1876 | 1073832133 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1876 | 1073832133 | UTILITY | COMMIT
      1 | 1878 | 1073832133 | QUERY   | INSERT INTO test_table_b values (40);
      1 | 1878 | 1073832133 | UTILITY | COMMIT
(8 rows)
```

Dieses Beispiel veranschaulicht die Arbeit mit Cursor-Schleifen. Die Tabelle test\$1table\$1a weist drei Werte auf. Ziel ist es, die drei Werte zu durchlaufen und sie in die Tabelle test\$1table\$1b einzufügen. Wenn eine gespeicherte NONATOMIC-Prozedur auf folgende Weise erstellt wird, wird nach der Ausführung der INSERT-Anweisung in der ersten Schleife die Fehlermeldung „cursor "cur1" does not exist“ (Cursor „cur1“ ist nicht vorhanden) ausgegeben. Dies liegt daran, dass das Autocommit von INSERT den geöffneten Cursor schließt.

```
insert into test_table_a values (1), (2), (3);

CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
  cur1 cursor for select * from test_table_a order by 1;
BEGIN
  open cur1;
  Loop
    fetch cur1 into rec;
    exit when not found;
    raise info '%', rec.v;
    insert into test_table_b values (rec.v);
  End Loop;
END
$$;

CALL sp_nonatomic_cursor();

INFO:  1
ERROR:  cursor "cur1" does not exist
CONTEXT:  PL/pgSQL function "sp_nonatomic_cursor" line 7 at fetch
```

Damit die Cursor-Schleife funktioniert, setzen Sie sie zwischen START TRANSACTION...COMMIT.

```
insert into test_table_a values (1), (2), (3);

CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
  cur1 cursor for select * from test_table_a order by 1;
BEGIN
  START TRANSACTION;
  open cur1;
  Loop
    fetch cur1 into rec;
    exit when not found;
    raise info '%', rec.v;
    insert into test_table_b values (rec.v);
  End Loop;
  COMMIT;
END
$$;

CALL sp_nonatomic_cursor();

INFO:  1
INFO:  2
INFO:  3
CALL
```

# Aufspüren von Fehlern
<a name="stored-procedure-trapping-errors"></a>

In diesem Thema wird beschrieben, wie Amazon Redshift mit Fehlern umgeht.

Wenn eine Abfrage oder ein Befehl in einer gespeicherten Prozedur einen Fehler verursacht, werden nachfolgende Abfragen nicht ausgeführt und die Transaktion wird rückgängig gemacht. Sie können Fehler jedoch unter Verwendung eines AUSNAHMEBLOCKS beheben.

**Anmerkung**  
Standardmäßig führt ein Fehler dazu, dass nachfolgende Abfragen nicht ausgeführt werden, auch wenn die gespeicherte Prozedur keine zusätzlichen Bedingungen enthält, die zu Fehlern führen.

```
[ <<label>> ]
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  WHEN OTHERS THEN
    statements
END;
```

Wenn eine Ausnahme auftritt und Sie einen Block zur Ausnahmebehandlung hinzufügen, können Sie RAISE-Anweisungen und die meisten anderen PL/pgSQL Anweisungen schreiben. Sie können beispielsweise eine Ausnahme mit einer benutzerdefinierten Nachricht auslösen oder einen Datensatz in eine Protokollierungstabelle einfügen.

Bei der Eingabe des Ausnahmebehandlungsblocks wird ein Rollback für die aktuelle Transaktion ausgeführt und eine neue Transaktion erstellt, um die Anweisungen im Block auszuführen. Wenn die Anweisungen im Block fehlerfrei ausgeführt werden, wird ein Commit der Transaktion ausgeführt und die Ausnahme wird erneut ausgelöst. Zuletzt wird die gespeicherte Prozedur beendet.

Die einige unterstützte Bedingung in einem Ausnahmeblock ist OTHERS, die auf jeden Fehlertyp außer Abfrageabbruch zutrifft. Tritt ein Fehler in einem Ausnahmehandhabungsblock auf, kann er auch von einem äußeren Ausnahmebehandlungsblock erfasst werden.

Ein Fehler innerhalb der NONATOMIC-Prozedur wird nicht erneut ausgelöst, wenn er durch einen Ausnahmeblock behandelt wird. Sehen Sie sich die PL/pgSQL Anweisung an`RAISE`, um eine vom Ausnahmebehandlungsblock abgefangene Ausnahme auszulösen. Diese Anweisung ist nur in Ausnahmebehandlungsblöcken gültig. Weitere Informationen finden Sie unter [RAISE](c_PLpgSQL-statements.md#r_PLpgSQL-messages-errors).

**Steuern, was nach einem Fehler in einer gespeicherten Prozedur geschieht, mit dem CONTINUE-Handler**

 Der `CONTINUE`-Handler ist eine Art Ausnahme-Handler, der den Ausführungsablauf innerhalb einer gespeicherten NONATOMIC-Prozedur steuert. Damit können Sie Ausnahmen erfassen und behandeln, ohne den vorhandenen Anweisungsblock zu beenden. Wenn in einer gespeicherten Prozedur ein Fehler auftritt, wird der Ablauf normalerweise unterbrochen und der Fehler wird an den Aufrufer zurückgegeben. In manchen Anwendungsfällen ist der Fehler jedoch nicht schwerwiegend genug, um eine Unterbrechung des Datenflusses zu rechtfertigen. Möglicherweise möchten Sie den Fehler sorgfältig behandeln, indem Sie eine Fehlerbehandlungslogik Ihrer Wahl in einer separaten Transaktion verwenden und dann weitere Anweisungen ausführen, die auf den Fehler folgen. Nachfolgend wird die Syntax dargestellt. 

```
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  [ CONTINUE_HANDLER | EXIT_HANDLER ] WHEN OTHERS THEN
    handler_statements
END;
```

Es sind mehrere Systemtabellen verfügbar, die Informationen über verschiedene Arten von Fehlern bereitstellen. Weitere Informationen finden Sie unter [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md), [STL\$1ERROR](r_STL_ERROR.md) und [SYS\$1STREAM\$1SCAN\$1ERRORS](r_SYS_STREAM_SCAN_ERRORS.md). Es gibt auch zusätzliche Systemtabellen, die Sie zur Fehlerbehebung verwenden können. Weitere Informationen dazu finden Sie unter [Referenz zu Systemtabellen und Ansichten](cm_chap_system-tables.md).

## Beispiel
<a name="stored-procedure-trapping-errors-examples"></a>

Das folgende Beispiel zeigt, wie Anweisungen im Ausnahmebehandlungsblock geschrieben werden. Die gespeicherte Prozedur verwendet das standardmäßige Transaktionsverwaltungsverhalten.

```
CREATE TABLE employee (firstname varchar, lastname varchar);
INSERT INTO employee VALUES ('Tomas','Smith');
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp() AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp();

INFO:  An exception occurred.
ERROR:  column "invalid" does not exist
CONTEXT:  SQL statement "select invalid"
PL/pgSQL function "update_employee_sp" line 3 at execute statement
```

Wenn wir in diesem Beispiel `update_employee_sp` aufrufen, wird die Informationsmeldung *An exception occurred.* (Eine Ausnahme ist aufgetreten.) ausgelöst und die Fehlermeldung wird ins Protokoll `employee_error_log` der Protokollierungstabelle eingefügt. Die ursprüngliche Ausnahme wird erneut ausgelöst, bevor die gespeicherte Prozedur beendet wird. Die folgenden Abfragen zeigen Datensätze, die sich aus der Ausführung des Beispiels ergeben.

```
SELECT * from employee;

firstname | lastname 
-----------+----------
 Tomas     | Smith

SELECT * from employee_error_log;

          message                     
------------------------------------------------
 Error message: column "invalid" does not exist
```

Weitere Informationen über RAISE, einschließlich Formatierungshilfe und einer Liste der zusätzlichen Ebenen, finden Sie unter [Unterstützte PL/pgSQL-Anweisungen](c_PLpgSQL-statements.md).

Das folgende Beispiel zeigt, wie Anweisungen im Ausnahmebehandlungsblock geschrieben werden. Die gespeicherte Prozedur verwendet das NONATOMIC-Transaktionsverwaltungsverhalten. In diesem Beispiel wird nach Abschluss des Prozeduraufrufs kein Fehler an den Aufrufer zurückgegeben. Die UPDATE-Anweisung wird aufgrund des Fehlers in der nächsten Anweisung nicht rückgängig gemacht. Die Informationsmeldung wird ausgelöst und die Fehlermeldung wird in die Protokollierungstabelle eingefügt.

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

-- Create the SP in NONATOMIC mode
CREATE OR REPLACE PROCEDURE update_employee_sp_2() NONATOMIC AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_2();
INFO:  An exception occurred.
CALL

SELECT * from employee;

 firstname | lastname 
-----------+----------
 Adam      | Smith
(1 row)

SELECT * from employee_error_log;

                    message                     
------------------------------------------------
 Error message: column "invalid" does not exist
(1 row)
```

Dieses Beispiel veranschaulicht das Erstellen einer Prozedur mit zwei Unterblöcken. Wenn die gespeicherte Prozedur aufgerufen wird, wird der Fehler aus dem ersten Unterblock von ihrem Ausnahmebehandlungsblock behandelt. Nachdem der erste Unterblock abgeschlossen ist, fährt die Prozedur mit der Ausführung des zweiten Unterblocks fort. Aus dem Ergebnis geht vorher, dass nach Abschluss des Prozeduraufrufs kein Fehler ausgelöst wird. Die Operationen UPDATE und INSERT für die Tabelle employee wurden übernommen. Fehlermeldungen aus beiden Ausnahmeblöcken wurden in die Protokollierungstabelle eingefügt.

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp_3() NONATOMIC AS
$$
BEGIN
    BEGIN
        UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
        EXECUTE 'select invalid1';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the first block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
    BEGIN
        INSERT INTO employee VALUES ('Edie','Robertson');
        EXECUTE 'select invalid2';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the second block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_3();
INFO:  An exception occurred in the first block.
INFO:  An exception occurred in the second block.
CALL

SELECT * from employee;

 firstname | lastname  
-----------+-----------
 Adam      | Smith
 Edie      | Robertson
(2 rows)

SELECT * from employee_error_log;

                     message                     
-------------------------------------------------
 Error message: column "invalid1" does not exist
 Error message: column "invalid2" does not exist
(2 rows)
```

Das folgende Beispiel demonstriert, wie Sie den CONTINUE-Ausnahme-Handler nutzen. In diesem Beispiel werden zwei Tabellen erstellt und in einer gespeicherten Prozedur verwendet. Der CONTINUE-Handler steuert den Ausführungsablauf in einer gespeicherten Prozedur mit dem Transaktionsverwaltungsverhalten NONATOMIC.

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_1() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (2);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Rufen Sie die gespeicherte Prozedur auf:

```
CALL sp_exc_handling_1();
```

Der Ablauf ist wie folgt:

1. Ein Fehler tritt auf, weil versucht wird, einen inkompatiblen Datentyp in eine Spalte einzufügen. Die Steuerung geht an den EXCEPTION-Block über. Bei der Eingabe des Ausnahmebehandlungsblocks wird ein Rollback für die aktuelle Transaktion ausgeführt und eine neue Transaktion erstellt, um die Anweisungen darin auszuführen.

1. Wenn die Anweisungen in CONTINUE\$1HANDLER ohne Fehler ausgeführt werden, geht die Steuerung an die Anweisung über, die unmittelbar auf die Anweisung folgt, die die Ausnahme verursacht hat. (Wenn eine Anweisung in CONTINUE\$1HANDLER eine neue Ausnahme auslöst, können Sie sie mit einer Ausnahmebehandlungsroutine innerhalb des EXCEPTION-Blocks behandeln.)

Nachdem Sie die gespeicherte Beispielprozedur aufgerufen haben, enthalten die Tabellen die folgenden Datensätze:
+ Wenn Sie `SELECT * FROM tbl_1;` ausführen, werden zwei Datensätze zurückgegeben. Diese enthalten die Werte `1` und `2`.
+ Wenn Sie `SELECT * FROM tbl_error_logging;` ausführen, wird ein Datensatz mit den folgenden Werten zurückgegeben: *Aufgetretener Fehler*, *42703*, und *Spalte „val“ ist in tbl\$11 nicht vorhanden*.

Das folgende zusätzliche Beispiel zur Fehlerbehandlung verwendet sowohl einen EXIT-Handler als auch einen CONTINUE-Handler. Es erstellt zwei Tabellen: eine Datentabelle und eine Protokollierungstabelle. Außerdem wird eine gespeicherte Prozedur erstellt, die die Fehlerbehandlung demonstriert:

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_2() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    BEGIN
        INSERT INTO tbl_1 VALUES (100);
        -- Expect an error for the insert statement following, because of the invalid value
        INSERT INTO tbl_1 VALUES ("val");
        INSERT INTO tbl_1 VALUES (101);
    EXCEPTION EXIT_HANDLER WHEN OTHERS THEN
        INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
    END;
    INSERT INTO tbl_1 VALUES (2);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (3);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Nachdem Sie die gespeicherte Prozedur erstellt haben, rufen Sie sie wie folgt auf:

```
CALL sp_exc_handling_2();
```

Wenn im inneren Ausnahmeblock, der vom inneren Satz von BEGIN und END in Klammern eingeschlossen ist, ein Fehler auftritt, wird er vom EXIT-Handler behandelt. Alle Fehler, die im äußeren Block auftreten, werden vom CONTINUE-Handler behandelt. 

Nachdem Sie die gespeicherte Beispielprozedur aufgerufen haben, enthalten die Tabellen die folgenden Datensätze:
+ Wenn Sie `SELECT * FROM tbl_1;` ausführen, werden vier Datensätze mit den Werten 1, 2, 3 und 100 zurückgegeben.
+ Wenn Sie `SELECT * FROM tbl_error_logging;` ausführen, werden zwei Datensätze zurückgegeben. Sie haben die folgenden Werte: *Aufgetretener Fehler*, *42703*, und *Spalte „val“ ist in tbl\$11 nicht vorhanden*.

Wenn die Tabelle **tbl\$1error\$1logging** nicht existiert, wird eine Ausnahme ausgelöst.

Das folgende Beispiel demonstriert, wie Sie den CONTINUE-Ausnahme-Handler mit der FOR-Schleife nutzen. In diesem Beispiel werden zwei Tabellen erstellt und in einer FOR-Schleife in einer gespeicherten Prozedur verwendet. Die FOR-Schleife ist eine Ergebnismengenvariante, was bedeutet, dass sie über die Ergebnisse einer Abfrage iteriert:

```
CREATE TABLE tbl_1 (a int);
INSERT INTO tbl_1 VALUES (1), (2), (3);
CREATE TABLE tbl_2 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_loop() NONATOMIC AS
$$
DECLARE
 rec RECORD;
BEGIN
    FOR rec IN SELECT a FROM tbl_1
    LOOP
        IF rec.a = 2 THEN
            -- Expect an error for the insert statement following, because of the invalid value
            INSERT INTO tbl_2 VALUES("val");
        ELSE
            INSERT INTO tbl_2 VALUES (rec.a);
        END IF;
    END LOOP;
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Rufen Sie die gespeicherte Prozedur auf:

```
CALL sp_exc_handling_loop();
```

Nachdem Sie die gespeicherte Beispielprozedur aufgerufen haben, enthalten die Tabellen die folgenden Datensätze:
+  Wenn Sie `SELECT * FROM tbl_2;` ausführen, werden zwei Datensätze zurückgegeben. Diese enthalten die Werte 1 und 3.
+ Wenn Sie `SELECT * FROM tbl_error_logging;` ausführen, wird ein Datensatz mit den folgenden Werten zurückgegeben: *Aufgetretener Fehler*, *42703*, und *Spalte „val“ ist in tbl\$12 nicht vorhanden*.

Hinweise zur Verwendung des CONTINUE-Handlers:
+ Die Schlüsselwörter CONTINUE\$1HANDLER und EXIT\$1HANDLER können nur in gespeicherten NONATOMIC-Prozeduren verwendet werden.
+ Die Schlüsselwörter CONTINUE\$1HANDLER und EXIT\$1HANDLER sind optional. EXIT\$1HANDLER ist die Standardeinstellung.

# Protokollieren von gespeicherten Prozeduren
<a name="c_PLpgSQL-logging"></a>

In diesem Thema werden die gespeicherten Prozeduren und Ansichten beschrieben, die Amazon Redshift für die Protokollierung gespeicherter Prozeduren verwendet.

Einzelheiten über gespeicherte Prozeduren sind in den folgenden Systemtabellen und -ansichern protokolliert:
+ SVL\$1STORED\$1PROC\$1CALL – Einzelheiten über Start- und Endzeit der gespeicherten Prozeduraufrufe werden protokolliert, und ob der Aufruf vor dem Abschluss abgebrochen wurde. Weitere Informationen finden Sie unter [SVL\$1STORED\$1PROC\$1CALL](r_SVL_STORED_PROC_CALL.md).
+ SVL\$1STORED\$1PROC\$1MESSAGES – Nachrichten in gespeicherten Prozeduren, die von der RAISE-Abfrage ausgegeben werden, werden mit der entsprechenden Protokollierungsebene erfasst. Weitere Informationen finden Sie unter [SVL\$1STORED\$1PROC\$1MESSAGES](r_SVL_STORED_PROC_MESSAGES.md).
+ SVL\$1QLOG – Für jede Abfrage einer gespeicherten Prozedur wird die Abfrage-ID des Prozeduraufrufs protokolliert. Weitere Informationen finden Sie unter [SVL\$1QLOG](r_SVL_QLOG.md).
+ STL\$1UTILITYTEXT – Gespeicherte Prozeduraufrufe werden nach Abschluss protokolliert. Weitere Informationen finden Sie unter [STL\$1UTILITYTEXT](r_STL_UTILITYTEXT.md).
+ PG\$1PROC\$1INFO – Diese Systemkatalogansicht stellt Informationen über gespeicherte Prozeduren dar. Weitere Informationen finden Sie unter [PG\$1PROC\$1INFO](r_PG_PROC_INFO.md).

# Einschränkungen bei gespeicherten Prozeduren
<a name="stored-procedure-constraints"></a>

In diesem Thema werden Einschränkungen für in Amazon Redshift gespeicherte Prozeduren beschrieben.

Bei der Verwendung von in Amazon Redshift gespeicherten Prozeduren gelten folgende Überlegungen.

## Unterschiede zwischen Amazon Redshift und PostgreSQL bezüglich des Supports für gespeicherte Prozeduren
<a name="stored-procedure-differences"></a>

 Folgende Unterschiede gibt es zwischen dem Support für gespeicherte Prozeduren in Amazon Redshift und PostgreSQL:
+ Amazon Redshift unterstützt keine Subtransaktionen und bietet deshalb begrenzten Support für Ausnahmehandhabungsblöcke.

## Überlegungen und Limits
<a name="stored-procedure-limits"></a>

Folgende Überlegungen gelten für in Amazon Redshift gespeicherte Prozeduren:
+ Die maximale Anzahl von gespeicherten Prozeduren für eine Datenbank beträgt 10 000.
+ Der Quellcode einer Prozedur darf maximal 2 MB betragen.
+ Die maximale Anzahl expliziter und impliziter Cursor, die Sie gleichzeitig in einer Benutzersitzung öffnen können, ist eins. FOR-Loops, die über die Ergebnismenge einer SQL-Anweisung iterieren, öffnen implizite Cursor. Verschachtelte Cursor werden nicht unterstützt.
+ Explizite und implizite Cursors verfügen über dieselben Einschränkungen bezüglich der Ergebnismenge wie Standard-Amazon-Redshift-Cursors. Weitere Informationen finden Sie unter [Einschränkungen für Cursors](declare.md#declare-constraints). 
+ Die maximale Anzahl an Ebenen für verschachtelte Aufrufe ist 16.
+ Die maximale Anzahl an Prozedurparametern ist 32 für Eingabe- und 32 für Ausgabeparameter.
+ Die maximale Anzahl an Variablen in einer gespeicherten Prozedur ist 1.024.
+ Jeder SQL-Befehl, der einen eigenen Transaktionskontext erfordert, wird innerhalb einer gespeicherten Prozedur nicht unterstützt. Beispiele sind unter anderem:
  + PREPARE
  + CREATE/DROP DATABASE
  + CREATE EXTERNAL TABLE
  + VACUUM
  + SET LOCAL
  + ALTER TABLE APPEND
+ Der Aufruf der `registerOutParameter`-Methoden durch den Java Database Connectivity (JDBC)-Treiber wird nicht für den Datentyp `refcursor` unterstützt. Ein Beispiel für die Verwendung des `refcursor`-Datentyps finden Sie unter [Rückgabe eines Ergebnissatzes von einer gespeicherten Prozedur](stored-procedure-result-set.md).

# PL/pgSQL-Sprachreferenz
<a name="c_pl_pgSQL_reference"></a>

Gespeicherte Prozeduren in Amazon Redshift basieren auf der prozeduralen PostgreSQL-PL/pgSQL-Sprache. Es gibt jedoch ein paar wichtige Unterschiede. In dieser Referenz finden Sie Details zur PL/pgSQL-Syntax, so wie sie von Amazon Redshift implementiert wurde. Weitere Informationen zu PL/pgSQL finden Sie unter [PL/pgSQL – Prozedurale SQL-Sprache](https://www.postgresql.org/docs/8.0/plpgsql.html) in der PostgreSQL-Dokumentation.

**Topics**
+ [Konventionen für die PL/pgSQL-Referenz](c_PL_reference_conventions.md)
+ [Struktur von PL/pgSQL](c_PLpgSQL-structure.md)
+ [Unterstützte PL/pgSQL-Anweisungen](c_PLpgSQL-statements.md)

# Konventionen für die PL/pgSQL-Referenz
<a name="c_PL_reference_conventions"></a>

In diesem Abschnitt finden Sie die Konventionen zum Schreiben der Syntax für die gespeicherte prozedurale PL/pgSQL-Sprache. 

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/de_de/redshift/latest/dg/c_PL_reference_conventions.html)

# Struktur von PL/pgSQL
<a name="c_PLpgSQL-structure"></a>

PL/pgSQL ist eine prozedurale Sprache mit vielen der gleichen Konstrukte wie andere prozedurale Sprachen. 

**Topics**
+ [Block](#r_PLpgSQL-block)
+ [Variablendeklaration](#r_PLpgSQL-variable-declaration)
+ [Aliasdeklaration](#r_PLpgSQL-alias-declaration)
+ [Integrierte Variablen](#r_PLpgSQL-builtin-variables)
+ [Datensatztypen](#r_PLpgSQL-record-type)

## Block
<a name="r_PLpgSQL-block"></a>

PL/pgSQL ist eine Sprache in Blockstruktur. Der gesamte Text einer Prozedur ist in einem Block definiert, der variable Deklarationen und PL/pgSQL-Anweisungen enthält. Eine Anweisung kann auch ein verschachtelter Block oder Subblock sein. 

Deklarationen und Anweisungen müssen mit einem Doppelpunkt enden. Dem END-Schlüsselwort in einem Block oder Subblock muss ein Doppelpunkt folgen. Verwenden Sie keine Doppelpunkte nach den Schlüsselwörtern DECLARE und BEGIN. 

Sie können alle Schlüsselwörter und Kennungen sowohl in Groß- als auch Kleinbuchstaben schreiben. Kennungen werden implizit in Kleinbuchstaben konvertiert, außer sie werden in doppelte Anführungszeichen eingeschlossen.

Ein doppelter Bindestrich (--) beginnt einen Kommentar, der bis zum Ende der Zeile erweitert wird. Ein /\$1 beginnt einen Blockkommentar, der bis zum nächsten Vorkommen von \$1/ erweitert wird. Blockkommentare können nicht verschachtelt werden. Sie können jedoch Kommentare mit doppelten Bindestrichen in einem Blockkommentar einschließen und ein doppelter Bindestrich kann die Blockkommentar-Trennzeichen /\$1 und \$1/ ausblenden.

Jede Anweisung im Anweisungsbereich eines Blocks kann ein Subblock sein. Sie können Subblöcke für die logische Gruppierung oder zum Lokalisieren von Variablen in eine kleine Gruppe von Anweisungen verwenden.

```
[ <<label>> ]
[ DECLARE
  declarations ]
BEGIN
  statements
END [ label ];
```

Die im Deklarationsbereich vor einem Block deklarierten Variablen werden jedes Mal zu ihren Standardwerten initialisiert, wenn der Block eingegeben wird. Mit anderen Worten, sie werden nicht nur einmal pro Funktionsaufruf initialisiert.

Es folgt ein Beispiel.

```
CREATE PROCEDURE update_value() AS $$
DECLARE
  value integer := 20;
BEGIN
  RAISE NOTICE 'Value here is %', value;  -- Value here is 20
  value := 50;
  --
  -- Create a subblock
  --
  DECLARE
    value integer := 80;
  BEGIN
    RAISE NOTICE 'Value here is %', value;  -- Value here is 80
  END;

  RAISE NOTICE 'Value here is %', value;  -- Value here is 50
END;
$$ LANGUAGE plpgsql;
```

Verwenden Sie eine Bezeichnung zur Identifizierung des Blocks für eine EXIT-Anweisung oder zur Qualifizierung der Namen der Variablen, die im Block deklariert wurden.

Verwechseln Sie nicht die Verwendung von BEGIN/END für die Gruppierung von Anweisungen in PL/pgSQL mit den Datenbankbefehlen für die Transaktionskontrolle. BEGIN und END dienen in PL/pgSQL lediglich der Gruppierung. Sie beginnen weder eine Transaktion, noch beenden sie eine.

## Variablendeklaration
<a name="r_PLpgSQL-variable-declaration"></a>

Deklarieren Sie alle Variablen in einem Block, mit Ausnahme der Loop-Variablen, im DECLARE-Bereich des Blocks. Variablen verwenden jeden gültigen Amazon-Redshift-Datentyp. Informationen zu unterstützten Datentypen finden Sie unter [Datentypen](c_Supported_data_types.md). 

Bei PL/pgSQL-Variablen kann es sich um jeden von Amazon Redshift unterstützten Datentyp handeln sowie `RECORD` und `refcursor`. Mehr über `RECORD` erfahren Sie unter [Datensatztypen](#r_PLpgSQL-record-type). Mehr über `refcursor` erfahren Sie unter [Cursor](c_PLpgSQL-statements.md#r_PLpgSQL-cursors). 

```
DECLARE
name [ CONSTANT ] type [ NOT NULL ] [ { DEFAULT | := } expression ];
```

Im Folgenden finden Sie Beispiele für Variablendeklarationen.

```
customerID integer;
numberofitems numeric(6);
link varchar;
onerow RECORD;
```

Die Loop-Variable eines FOR-Loops, die über einen Bereich von Ganzzahlen iteriert, wird automatisch als Ganzzahlvariable deklariert. 

Die DEFAULT-Klausel gibt, wenn angegeben, den Anfangswert an, welcher der Variablen beim Eingeben des Blocks zugewiesen wird. Wenn die DEFAULT-Klausel nicht angegeben wird, dann wird die Variable zum SQL-NULL-Wert initialisiert. Die CONSTANT-Option verhindert, dass die Variable zugewiesen wird, sodass ihr Wert für die Dauer des Blocks konstant bleibt. Wenn NOT NULL festgelegt wird, führt eine Zuweisung eines Null-Werts zu einem Laufzeitfehler. Für alle Variablen, die als NOT NULL deklariert sind, muss ein standardmäßiger Nicht-Null-Wert angegeben sein.

Der Standardwert wird bei jeder Eingabe des Blocks ausgewertet. Wenn Sie beispielsweise `now()` einer Variablen des Typs `timestamp` zuweisen, weist die Variable den Zeitpunkt des aktuellen Funktionsaufrufs auf und nicht den Zeitpunkt, als die Funktion vorkompiliert wurde.

```
quantity INTEGER DEFAULT 32;
url VARCHAR := 'http://mysite.com';
user_id CONSTANT INTEGER := 10;
```

Der `refcursor`-Datentyp ist der Datentyp von Cursor-Variablen in gespeicherten Prozeduren. Ein `refcursor`-Wert kann von innerhalb einer gespeicherten Prozedur zurückgegeben werden. Weitere Informationen finden Sie unter [Rückgabe eines Ergebnissatzes von einer gespeicherten Prozedur](stored-procedure-result-set.md).

## Aliasdeklaration
<a name="r_PLpgSQL-alias-declaration"></a>

Wenn die Signatur der gespeicherten Prozedur den Argumentnamen weg lässt, können Sie einen Alias für das Argument deklarieren.

```
name ALIAS FOR $n;
```

## Integrierte Variablen
<a name="r_PLpgSQL-builtin-variables"></a>

Die folgenden integrierten Variablen werden unterstützt:
+ FOUND
+ SQLSTATE
+ SQLERRM
+ GET DIAGNOSTICS integer\$1var := ROW\$1COUNT;

FOUND ist eine spezielle Variable vom Typ Boolesch. FOUND beginnt in jedem Prozeduraufruf mit „false“. FOUND wird von den folgenden Anweisungen festgelegt:
+ SELECT INTO

  Legt FOUND auf „true“ fest, wenn eine Zeile zurückgegeben wird, und auf „false“, wenn keine Zeile zurückgegeben wird.
+ UPDATE, INSERT und DELETE

  Legt FOUND auf „true“ fest, wenn mindestens eine Zeile betroffen ist, und auf „false“, wenn keine Zeile betroffen ist.
+ FETCH

  Legt FOUND auf „true“ fest, wenn eine Zeile zurückgegeben wird, und auf „false“, wenn keine Zeile zurückgegeben wird.
+ FOR-Anweisung

  Legt FOUND auf „true“ fest, wenn die FOR-Anweisung einmal oder mehrmals iteriert, und ansonsten auf „false“. Dies gilt für alle drei Varianten der FOR-Anweisung: Ganzzahl-FOR-Loops, Datensatz-FOR-Loops und dynamische Datensatz-FOR-Loops. 

  FOUND wird beim Beenden des FOR-Loops festgelegt. Innerhalb der Laufzeit des Loops wird FOUND nicht von der FOR-Anweisung modifiziert. Es kann jedoch durch die Ausführung anderer Anweisungen im Loop-Text geändert werden.

Es folgt ein Beispiel.

```
CREATE TABLE employee(empname varchar);
CREATE OR REPLACE PROCEDURE show_found()
AS  $$
DECLARE
  myrec record;
BEGIN
  SELECT INTO myrec * FROM employee WHERE empname = 'John';
  IF NOT FOUND THEN
    RAISE EXCEPTION 'employee John not found';
  END IF;
END;
$$ LANGUAGE plpgsql;
```

Innerhalb eines Ausnahmehandlers enthält die spezielle Variable SQLSTATE den Fehlercode, welcher der Ausnahme entspricht, die ausgelöst wurde. Die spezielle Variable SQLERRM enthält die mit der Ausnahme verbundene Fehlermeldung. Diese Variablen sind außerhalb von Ausnahmehandlern undefiniert und zeigen bei Verwendung einen Fehler an.

Es folgt ein Beispiel.

```
CREATE OR REPLACE PROCEDURE sqlstate_sqlerrm() AS
$$
BEGIN
  UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
  EXECUTE 'select invalid';
  EXCEPTION WHEN OTHERS THEN
  RAISE INFO 'error message SQLERRM %', SQLERRM;
  RAISE INFO 'error message SQLSTATE %', SQLSTATE;
END;
$$ LANGUAGE plpgsql;
```

ROW\$1COUNT wird mit dem Befehl GET DIAGNOSTICS verwendet. Sie zeigt die Anzahl der Spalten an, die vom letzten SQL-Befehl verarbeitet wurden, der an die SQL-Engine gesendet wurde.

Es folgt ein Beispiel.

```
CREATE OR REPLACE PROCEDURE sp_row_count() AS
$$
DECLARE
  integer_var int;
BEGIN
  INSERT INTO tbl_row_count VALUES(1);
  GET DIAGNOSTICS integer_var := ROW_COUNT;
  RAISE INFO 'rows inserted = %', integer_var;
END;
$$ LANGUAGE plpgsql;
```

## Datensatztypen
<a name="r_PLpgSQL-record-type"></a>

Ein RECORD-Typ ist kein echter Datensatztyp, sondern nur ein Platzhalter. Variablen vom Datensatztyp übernehmen die tatsächliche Zeilenstruktur der Zeile, der sie während des SELECT- oder FOR-Befehls zugeordnet werden. Die Unterstruktur einer Datensatzvariablen kann sich bei jeder Zuordnung zu einem Wert ändern. Eine Datensatzvariable verfügt erst dann über eine Unterstruktur, wenn sie zum ersten Mal zugewiesen wird. Bei jedem Versuch auf ein darin enthaltenes Feld zuzugreifen, wird ein Laufzeitfehler zurückgegeben.

```
name RECORD;
```

Es folgt ein Beispiel.

```
CREATE TABLE tbl_record(a int, b int);
INSERT INTO tbl_record VALUES(1, 2);
CREATE OR REPLACE PROCEDURE record_example()
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
BEGIN
  FOR rec IN SELECT a FROM tbl_record
  LOOP
    RAISE INFO 'a = %', rec.a;
  END LOOP;
END;
$$;
```

# Unterstützte PL/pgSQL-Anweisungen
<a name="c_PLpgSQL-statements"></a>

 PL/pgSQL-Anweisungen erweitern SQL-Befehle mit prozeduralen Konstrukten, einschließlich Schleifen- und bedingten Ausdrücken, um den logischen Fluss zu steuern. Die meisten SQL-Befehle können verwendet werden, einschließlich der Data Manipulation Language (DML) wie COPY, UNLOAD und INSERT, und der Data Definition Language (DDL) wie CREATE TABLE. Eine Liste umfassender SQL-Befehle finden Sie unter [SQL-Befehle](c_SQL_commands.md). Darüber hinaus werden die folgenden PL/pgSQL-Anweisungen von Amazon Redshift unterstützt. 

**Topics**
+ [Zuweisung](#r_PLpgSQL-assignment)
+ [SELECT INTO](#r_PLpgSQL-select-into)
+ [No-op](#r_PLpgSQL-no-op)
+ [Dynamisches SQL](#r_PLpgSQL-dynamic-sql)
+ [Ergebnis](#r_PLpgSQL-return)
+ [Bedingungen: IF](#r_PLpgSQL-conditionals-if)
+ [Bedingungen: CASE](#r_PLpgSQL-conditionals-case)
+ [Loops](#r_PLpgSQL-loops)
+ [Cursor](#r_PLpgSQL-cursors)
+ [RAISE](#r_PLpgSQL-messages-errors)
+ [Transaktionskontrolle](#r_PLpgSQL-transaction-control)

## Zuweisung
<a name="r_PLpgSQL-assignment"></a>

Die Zuweisungsanweisung ordnet einer Variablen einen Wert zu. Der Ausdruck muss einen einzelnen Wert zurückgeben.

```
identifier := expression;
```

Die Verwendung des nicht standardmäßigen `=` für die Anweisung, anstelle von `:=`, wird auch akzeptiert.

Wenn der Datentyp des Ausdrucks nicht mit dem Datentyp der Variablen übereinstimmt oder die Variable eine Größe oder Genauigkeit aufweist, wird der Ergebniswert implizit konvertiert.

Es folgen Beispiele.

```
customer_number := 20;
tip := subtotal * 0.15;
```

## SELECT INTO
<a name="r_PLpgSQL-select-into"></a>

Die SELECT INTO-Anweisung weist das Ergebnis mehrerer Spalten (jedoch nur eine Zeile) einer Datensatzvariablen oder Liste von skalaren Variablen zu.

```
SELECT INTO target select_expressions FROM ...;
```

In der vorhergehenden Syntax kann *Ziel* eine Datensatzvariable oder eine durch Kommata getrennte Liste einfacher Variablen und Datensatzfelder sein. Die *select\$1expressions*-Liste und der Rest des Befehls sind die gleichen wie bei regulärem SQL.

Wenn eine Variablenliste als *Ziel* verwendet wird, müssen die ausgewählten Werte genau der Struktur des Ziels entsprechen oder ein Laufzeitfehler tritt auf. Wenn eine Datensatzvariable das Ziel ist, konfiguriert sie sich automatisch für den Zeilentyp der Abfrageergebnisspalten.

Die INTO-Klausel kann fast überall in der SELECT-Anweisung erscheinen. Sie wird normalerweise direkt nach der SELECT-Klausel oder direkt vor der FROM-Klausel angezeigt. Das heißt, sie erscheint direkt vor oder nach der *select\$1expressions*-Liste.

Wenn die Abfrage null Zeilen ausgibt, werden dem *Ziel* NULL-Werte zugewiesen. Wenn die Abfrage mehrere Zeilen ausgibt, wird die erste Zeile dem *Ziel* zugewiesen und der Rest wird verworfen. Sofern die Anweisung kein ORDER BY enthält, ist die erste Zeile nicht deterministisch.

Um festzustellen, ob die Anweisung mindestens eine Zeile zurückgegeben hat, verwenden Sie die spezielle FOUND-Variable.

```
SELECT INTO customer_rec * FROM cust WHERE custname = lname;
IF NOT FOUND THEN
  RAISE EXCEPTION 'employee % not found', lname;
END IF;
```

Um herauszufinden, ob ein Datensatzergebnis null ist, können Sie die IS NULL-Bedingung verwenden. Es gibt keine Möglichkeit, zu bestimmen, ob zusätzliche Zeilen verworfen wurden. Das folgende Beispiel behandelt den Fall, bei dem keine Zeilen zurückgegeben wurden.

```
CREATE OR REPLACE PROCEDURE select_into_null(return_webpage OUT varchar(256))
AS $$
DECLARE
  customer_rec RECORD;
BEGIN
  SELECT INTO customer_rec * FROM users WHERE user_id=3;
  IF customer_rec.webpage IS NULL THEN
    -- user entered no webpage, return "http://"
    return_webpage = 'http://';
  END IF;
END;
$$ LANGUAGE plpgsql;
```

## No-op
<a name="r_PLpgSQL-no-op"></a>

Die no-op-Anweisung (`NULL;`) ist eine Platzhalteranweisung, die nichts tut. Eine no-op-Anweisung kann anzeigen, dass eine Verzweigung einer IF-THEN-ELSE-Kette leer ist.

```
NULL;
```

## Dynamisches SQL
<a name="r_PLpgSQL-dynamic-sql"></a>

Zum Generieren dynamischer Befehle, die jedes Mal verschiedene Tabellen oder unterschiedliche Datentypen umfassen können, wenn sie von einer gespeicherten PL/pgSQL-Prozedur ausgeführt werden, verwenden Sie die `EXECUTE`-Anweisung.

```
EXECUTE command-string [ INTO target ];
```

Im vorhergehenden Beispiel ist *command-string* ein Ausdruck, der eine Zeichenfolge (vom Typ Text) zum Ergebnis hat, die den auszuführenden Befehl enthält. Dieser *command-string*-Wert wird an die SQL-Engine gesendet. Es wird keine Ersetzung von PL/pgSQL-Variablen für die Befehlszeichenfolge vorgenommen. Die Werte von Variablen müssen in die Befehlszeichenfolge eingefügt werden, während sie erstellt wird.

**Anmerkung**  
Sie können COMMIT- und ROLLBACK-Anweisungen nicht innerhalb von dynamischem SQL verwenden. Informationen zur Verwendung von COMMIT- und ROLLBACK-Anweisungen innerhalb eines gespeicherten Verfahrens finden Sie unter [Verwalten von Transaktionen](stored-procedure-transaction-management.md). 

Bei der Arbeit mit dynamischen Befehlen müssen Sie sich häufig um das Escaping von einfachen Anführungszeichen kümmern. Wir empfehlen, dass Sie festen Text im Funktionstext mit der Dollaranführung in Anführungszeichen einschließen. Dynamische Werte, die in eine erstellte Abfrage eingefügt werden sollen, erfordern eine spezielle Vorgehensweisen, da sie selber Anführungszeichen enthalten können. Das folgende Beispiel setzt die Dollaranführung für die gesamte Funktion voraus, daher müssen keine doppelten Anführungszeichen verwendet werden.

```
EXECUTE 'UPDATE tbl SET '
  || quote_ident(colname)
  || ' = '
  || quote_literal(newvalue)
  || ' WHERE key = '
  || quote_literal(keyvalue);
```

Das vorhergehende Beispiel zeigt die Funktionen `quote_ident(text)` und `quote_literal(text)`. Dieses Beispiel leitet Variablen, die Spalten- und Tabellenkennungen enthalten, an die `quote_ident`-Funktion weiter. Es übermittelt auch Variablen, die Literalzeichenfolgen im erstellten Befehl enthalten, an die `quote_literal`-Funktion. Beide Funktionen unternehmen alle erforderlichen Schritte, um den in doppelten oder einfachen Anführungszeichen eingeschlossenen Eingabetext entsprechend zurückzugeben. Dabei sind alle eingebetteten speziellen Zeichen ordnungsgemäß mit einem Escape-Zeichen versehen.

Die Dollaranführung ist nur nützlich, wenn Sie Anführungszeichen für festen Text setzen. Schreiben Sie das vorhergehende Beispiel nicht in folgendem Format.

```
EXECUTE 'UPDATE tbl SET '
  || quote_ident(colname)
  || ' = $$'
  || newvalue
  || '$$ WHERE key = '
  || quote_literal(keyvalue);
```

Sie tun dies nicht, weil im Beispiel Fehler auftreten, wenn der Inhalt von `newvalue` zufällig \$1\$1 enthält. Dasselbe Problem gilt für alle anderen Dollaranführungstrennzeichen, die Sie auswählen können. Verwenden Sie die `quote_literal`-Funktion, um vorher nicht bekannten Text sicher mit Anführungszeichen zu versehen.

## Ergebnis
<a name="r_PLpgSQL-return"></a>

Die RETURN-Anweisung wird aus einer gespeicherten Prozedur zum Aufrufer zurückgegeben.

```
RETURN;
```

Es folgt ein Beispiel.

```
CREATE OR REPLACE PROCEDURE return_example(a int)
AS $$  
BEGIN
  FOR b in 1..10 LOOP
    IF b < a THEN
      RAISE INFO 'b = %', b;
    ELSE
      RETURN;
    END IF;
  END LOOP;
END;
$$ LANGUAGE plpgsql;
```

## Bedingungen: IF
<a name="r_PLpgSQL-conditionals-if"></a>

Die IF-Bedingungsanweisung kann folgende Formen in der PL/pgSQL-Sprache annehmen, die Amazon Redshift verwendet:
+ IF ... THEN

  ```
  IF boolean-expression THEN
    statements
  END IF;
  ```

  Es folgt ein Beispiel.

  ```
  IF v_user_id <> 0 THEN
    UPDATE users SET email = v_email WHERE user_id = v_user_id;
  END IF;
  ```
+ IF ... THEN ... ELSE

  ```
  IF boolean-expression THEN
    statements
  ELSE
    statements
  END IF;
  ```

  Es folgt ein Beispiel.

  ```
  IF parentid IS NULL OR parentid = ''
  THEN
    return_name = fullname;
    RETURN;
  ELSE
    return_name = hp_true_filename(parentid) || '/' || fullname;
    RETURN;
  END IF;
  ```
+ IF ... THEN ... ELSIF ... THEN ... ELSE 

  Das Schlüsselwort ELSIF kann auch als ELSEIF angegeben werden.

  ```
  IF boolean-expression THEN
    statements
  [ ELSIF boolean-expression THEN
    statements
  [ ELSIF boolean-expression THEN
    statements
      ...] ]
  [ ELSE
    statements ]
  END IF;
  ```

  Es folgt ein Beispiel.

  ```
  IF number = 0 THEN
    result := 'zero';
  ELSIF number > 0 THEN
    result := 'positive';
  ELSIF number < 0 THEN
    result := 'negative';
  ELSE
    -- the only other possibility is that number is null
    result := 'NULL';
  END IF;
  ```

## Bedingungen: CASE
<a name="r_PLpgSQL-conditionals-case"></a>

Die CASE-Bedingungsanweisung kann folgende Formen in der PL/pgSQL-Sprache annehmen, die Amazon Redshift verwendet:
+ Einfaches CASE 

  ```
  CASE search-expression
  WHEN expression [, expression [ ... ]] THEN
    statements
  [ WHEN expression [, expression [ ... ]] THEN
    statements
    ... ]
  [ ELSE
    statements ]
  END CASE;
  ```

  Eine einfache CASE-Anweisung stellt eine Bedingungsausführung basierend auf der Gleichheit von Operanden bereit.

  Der *search-expression*-Wert wir ein Mal ausgewertet und aufeinanderfolgend mit jedem *Ausdruck* in der WHEN-Klausel verglichen. Bei einer Übereinstimmung werden die entsprechenden *Anweisungen* ausgeführt und die Kontrolle wird an die nächste Anweisung nach END CASE übergeben. Nachfolgende WHEN-Ausdrücke werden nicht ausgewertet. Bei keiner Übereinstimmung werden die ELSE-*Anweisungen* ausgeführt. Wenn ELSE jedoch nicht vorhanden ist, wird eine CASE\$1NOT\$1FOUND-Ausnahme ausgelöst.

  Es folgt ein Beispiel.

  ```
  CASE x
  WHEN 1, 2 THEN
    msg := 'one or two';
  ELSE
    msg := 'other value than one or two';
  END CASE;
  ```
+ Gesuchtes CASE 

  ```
  CASE
  WHEN boolean-expression THEN
    statements
  [ WHEN boolean-expression THEN
    statements
    ... ]
  [ ELSE
    statements ]
  END CASE;
  ```

  Die gesuchte CASE-Anweisung stellt Bedingungsausführungen basierend auf der Wahrheit von booleschen Ausdrücken bereit. 

  Der *boolean-expression* jeder WHEN-Klausel wird dann ausgewertet, bis einer gefunden wird, der als Ergebnis „true“ zurück gibt. Anschließend werden die entsprechenden Anweisungen ausgeführt und die Kontrolle wird an die nächste Anweisung nach END CASE übergeben. Nachfolgende WHEN-*Ausdrücke* werden nicht ausgewertet. Wenn kein „true“-Ergebnis gefunden wird, werden die ELSE-*Anweisungen* ausgeführt. Wenn ELSE jedoch nicht vorhanden ist, wird eine CASE\$1NOT\$1FOUND-Ausnahme ausgelöst.

  Es folgt ein Beispiel.

  ```
  CASE
  WHEN x BETWEEN 0 AND 10 THEN
    msg := 'value is between zero and ten';
  WHEN x BETWEEN 11 AND 20 THEN
    msg := 'value is between eleven and twenty';
  END CASE;
  ```

## Loops
<a name="r_PLpgSQL-loops"></a>

Loop-Anweisungen können folgende Formen in der PL/pgSQL-Sprache annehmen, die Amazon Redshift verwendet:
+ Einfacher Loop 

  ```
  [<<label>>]
  LOOP
    statements
  END LOOP [ label ];
  ```

  Ein einfacher Loop definiert einen bedingungslosen Loop, der unbegrenzt wiederholt wird, bis er von einer EXIT- oder RETURN-Anweisung beendet wird. Die optionale Bezeichnung kann von EXIT- und CONTINUE-Anweisungen in verschachtelten Loops verwendet werden, um anzugeben, auf welchen Loop sich die EXIT- und CONTINUE-Anweisungen beziehen.

  Es folgt ein Beispiel.

  ```
  CREATE OR REPLACE PROCEDURE simple_loop()
  LANGUAGE plpgsql
  AS $$
  BEGIN
    <<simple_while>>
    LOOP
      RAISE INFO 'I am raised once';  
      EXIT simple_while;
      RAISE INFO 'I am not raised';
    END LOOP;
    RAISE INFO 'I am raised once as well';
  END;
  $$;
  ```
+ Exit-Loop

  ```
  EXIT [ label ] [ WHEN expression ];
  ```

  Wenn *Bezeichnung* nicht vorhanden ist, wird der innerste Loop beendet und die Anweisung als nächstes ausgeführt, die dem END LOOP folgt. Wenn *Bezeichnung* vorhanden ist, muss es sich um die Bezeichnung des aktuellen oder eines verschachtelten Loops oder Blocks der äußeren Ebene handeln. Anschließend wird der benannte Loop oder Block beendet und die Kontrolle fährt mit der Anweisung nach dem entsprechenden END des Loops oder Blocks fort.

  Wenn WHEN angegeben ist, wird der Loop nur beendet, wenn *Ausdruck* „true“ lautet. Andernfalls wird die Kontrolle an die Anweisung nach EXIT weitergeleitet.

  Sie können EXIT mit allen Arten von Loops verwenden. Es ist nicht auf die Nutzung mit bedingungslosen Loops beschränkt.

  Bei Verwendung mit einem BEGIN-Block übergibt EXIT die Kontrolle an die nächste Anweisung nach dem Ende des Blocks. Zu diesem Zweck muss eine Bezeichnung verwendet werden. Ein nicht gekennzeichnetes EXIT gilt nie als übereinstimmend mit einem BEGIN-Block.

  Es folgt ein Beispiel.

  ```
  CREATE OR REPLACE PROCEDURE simple_loop_when(x int)
  LANGUAGE plpgsql
  AS $$
  DECLARE i INTEGER := 0;
  BEGIN
    <<simple_loop_when>>
    LOOP
      RAISE INFO 'i %', i;
      i := i + 1;
      EXIT simple_loop_when WHEN (i >= x);
    END LOOP;
  END;
  $$;
  ```
+ Continue-Loop 

  ```
  CONTINUE [ label ] [ WHEN expression ];
  ```

  Wenn *Bezeichnung* nicht angegeben ist, springt die Ausführung zur nächsten Iteration des innersten Loops. Das heißt, alle im Loop-Text verbleibenden Anweisungen werden übersprungen. Die Kontrolle geht dann an den Loop-Kontrollausdruck (falls vorhanden) zurück, um festzulegen, ob eine weitere Loop-Iteration erforderlich ist. Wenn *Bezeichnung* vorhanden ist, wird die Bezeichnung des Loops angegeben, dessen Ausführung fortgesetzt wird.

  Wenn WHEN angegeben ist, wird die nächste Iteration des Loops nur dann begonnen, wenn *Ausdruck* „true“ lautet. Andernfalls wird die Kontrolle an die Anweisung nach CONTINUE weitergeleitet.

  Sie können CONTINUE mit allen Arten von Loops verwenden. Es ist nicht auf die Nutzung mit bedingungslosen Loops beschränkt.

  ```
  CONTINUE mylabel;
  ```
+ WHILE-Loop 

  ```
  [<<label>>]
  WHILE expression LOOP
    statements
  END LOOP [ label ];
  ```

  Die WHILE-Anweisung wiederholt eine Reihenfolge von Anweisungen solange der *boolean-expression* als „true“ ausgewertet wird. Der Ausdruck wird kurz vor jedem Eintritt in den Loop-Text geprüft.

  Es folgt ein Beispiel.

  ```
  WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
    -- some computations here
  END LOOP;
  
  WHILE NOT done LOOP
    -- some computations here
  END LOOP;
  ```
+ FOR-Loop (Ganzzahlvariante) 

  ```
  [<<label>>]
  FOR name IN [ REVERSE ] expression .. expression LOOP
    statements
  END LOOP [ label ];
  ```

  Der FOR-Loop (Ganzzahlvariante) erstellt einen Loop, der über einen Bereich von Ganzzahlwerten iteriert. Der Variablenname wird automatisch als Typ Ganzzahl definiert und existiert nur innerhalb des Loops. Jede vorhandene Definition des Variablennamen wird innerhalb des Loops ignoriert. Die zwei Ausdrücke, welche die Unter- und Obergrenze des Bereichs angeben, werden beim Eintritt in den Loop einmal ausgewertet. Wenn Sie REVERSE angeben, wird der Schrittwert nach jeder Iteration eher subtrahiert als addiert.

  Wenn die Untergrenze größer ist als die Obergrenze (oder kleiner, im REVERSE-Fall), wird der Loop-Text nicht ausgeführt. Es wird kein Fehler ausgegeben.

  Wenn eine Bezeichnung einem FOR-Loop angefügt ist, können Sie unter Verwendung dieser Bezeichnung mit einem qualifizierten Namen auf die Ganzzahl-Loop-Variable verweisen.

  Es folgt ein Beispiel.

  ```
  FOR i IN 1..10 LOOP
    -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop
  END LOOP;
  
  FOR i IN REVERSE 10..1 LOOP
    -- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loop
  END LOOP;
  ```
+ FOR-Loop (Ergebnissatzvariante) 

  ```
  [<<label>>]
  FOR target IN query LOOP
    statements
  END LOOP [ label ];
  ```

  Das *Ziel* ist eine Datensatzvariable oder eine durch Kommata getrennte Liste skalarer Variablen. Das Ziel wird jeder Zeile aufeinanderfolgend zugewiesen, die aus der Abfrage resultiert, und der Loop-Text wird für jede Abfrage ausgeführt.

  Der FOR-Loop (Ergebnissatzvariante) ermöglicht einer gespeicherten Prozedur, die Ergebnisse einer Abfrage zu durchlaufen und diese Daten entsprechend zu bearbeiten.

  Es folgt ein Beispiel.

  ```
  CREATE PROCEDURE cs_refresh_reports() AS $$
  DECLARE
    reports RECORD;
  BEGIN
    FOR reports IN SELECT * FROM cs_reports ORDER BY sort_key LOOP
      -- Now "reports" has one record from cs_reports
      EXECUTE 'INSERT INTO ' || quote_ident(reports.report_name) || ' ' || reports.report_query;
    END LOOP;
    RETURN;
  END;
  $$ LANGUAGE plpgsql;
  ```
+ FOR-Loop mit dynamischem SQL

  ```
  [<<label>>]
  FOR record_or_row IN EXECUTE text_expression LOOP 
    statements
  END LOOP;
  ```

  Der FOR-Loop mit dynamischem SQL ermöglicht einer gespeicherten Prozedur, die Ergebnisse einer dynamischen Abfrage zu durchlaufen und diese Daten entsprechend zu bearbeiten.

  Es folgt ein Beispiel.

  ```
  CREATE OR REPLACE PROCEDURE for_loop_dynamic_sql(x int)
  LANGUAGE plpgsql
  AS $$
  DECLARE
    rec RECORD;
    query text;
  BEGIN
    query := 'SELECT * FROM tbl_dynamic_sql LIMIT ' || x;
    FOR rec IN EXECUTE query
    LOOP
      RAISE INFO 'a %', rec.a;
    END LOOP;
  END;
  $$;
  ```

## Cursor
<a name="r_PLpgSQL-cursors"></a>

Statt eine ganze Abfrage auf einmal auszuführen, können Sie einen Cursor einrichten. Ein *Cursor *kapselt eine Abfrage und liest jeweils wenige Zeilen des Abfrageergebnisses. Ein Grund hierfür ist das Vermeiden von Speicherüberlauf, wenn das Ergebnis eine große Anzahl von Zeilen enthält. Ein weiterer Grund ist die Rückgabe einer Referenz an einen Cursor, den eine gespeicherte Prozedur erstellt hat. Dies erlaubt dem Aufrufer, die Zeilen zu lesen. Dieser Ansatz bietet eine effiziente Methode, große Zeilensätze von gespeicherten Prozeduren zurückzugeben.

Um Cursor in einer gespeicherten NONATOMIC-Prozedur zu verwenden, platzieren Sie die Cursor-Schleife zwischen START TRANSACTION...COMMIT.

Zum Einrichten eines Cursors deklarieren Sie zuerst eine Cursor-Variable. Jeder Zugriff auf Cursor in PL/pgSQL durchläuft Cursor-Variablen, die immer vom speziellen Datentyp sind `refcursor`. Ein `refcursor`-Datentyp hält einfach eine Referenz auf einen Cursor. 

Sie können eine Cursor-Variable erstellen, indem Sie sie als Variable des Typs deklarieren `refcursor`. Alternativ können Sie die folgende Cursor-Deklarationssyntax verwenden.

```
name CURSOR [ ( arguments ) ] FOR query ;
```

Im vorhergehenden Beispiel ist *Argumente* (wenn angegeben) eine durch Kommata getrennte Liste von *Name-Datentyp*-Paaren, die jeweils Namen definieren, die in *Abfrage* durch Parameterwerte ersetzt werden sollen. Die tatsächlichen Werte, durch die diese Namen ersetzt werden sollen, werden später beim Öffnen des Cursors angegeben.

Es folgen Beispiele.

```
DECLARE
  curs1 refcursor;
  curs2 CURSOR FOR SELECT * FROM tenk1;
  curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;
```

Alle drei dieser Variablen haben den Datentyp `refcursor`, aber die erste kann mit jeder Abfrage verwendet werden. Im Gegensatz dazu verfügt die zweite über eine vollständig angegebene Abfrage, die bereits an sie gebunden ist, und die letzte über eine an sie gebundene parametrisierte Abfrage. Der `key`-Wert wird durch einen Ganzzahl-Parameterwert ersetzt, wenn der Cursor geöffnet wird. Die Variable `curs1` gilt als *ungebunden, * weil sie nicht an eine bestimmte Abfrage gebunden ist.

Bevor Sie einen Cursor zum Abrufen von Zeilen verwenden können, muss er geöffnet werden. PL/pgSQL verfügt über drei Formen der OPEN-Anweisung. Zwei davon verwenden ungebundene Cursor-Variablen und die dritte eine gebundene Cursor-Variable:
+ Öffnen zum Auswählen: Die Cursor-Variable wird geöffnet und die angegebene Abfrage zur Ausführung zugeteilt. Der Cursor darf nicht bereits geöffnet sein. Außerdem muss er als ungebundener Cursor deklariert worden sein (das heißt, als eine einfache `refcursor`-Variable). Die SELECT-Abfrage wird genauso wie andere SELECT-Anweisungen in PL/pgSQL behandelt. 

  ```
  OPEN cursor_name FOR SELECT ...;                     
  ```

  Es folgt ein Beispiel.

  ```
  OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;    
  ```
+ Öffnen zum Ausführen: Die Cursor-Variable wird geöffnet und die angegebene Abfrage zur Ausführung zugeteilt. Der Cursor darf nicht bereits geöffnet sein. Außerdem muss er als ungebundener Cursor deklariert worden sein (das heißt, als eine einfache `refcursor`-Variable). Die Abfrage wird auf die gleiche Art und Weise als Zeichenfolgeausdruck angegeben wie im EXECUTE-Befehl. Dieser Ansatz bietet Flexibilität, sodass die Abfrage von einer Ausführung zur nächsten variieren kann.

  ```
  OPEN cursor_name FOR EXECUTE query_string;
  ```

  Es folgt ein Beispiel.

  ```
  OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);
  ```
+ Öffnen eines gebundenen Cursors: Diese Form von OPEN wird zum Öffnen einer Cursor-Variablen verwendet, deren Abfrage beim Deklarieren an sie gebunden wurde. Der Cursor darf nicht bereits geöffnet sein. Eine Liste der tatsächlichen Argumentwertausdrücke muss angezeigt werden, wenn und nur wenn der Cursor zur Verwendung von Argumenten deklariert wurde. Diese Werte werden in der Abfrage ersetzt. 

  ```
  OPEN bound_cursor_name [ ( argument_values ) ];
  ```

  Es folgt ein Beispiel.

  ```
  OPEN curs2;
  OPEN curs3(42);
  ```

Nachdem ein Cursor geöffnet wurde, können Sie mit ihm arbeiten, indem Sie die Anweisungen verwenden, die im Folgenden beschrieben sind. Diese Anweisungen müssen nicht in derselben gespeicherten Prozedur ausgeführt werden, die den Cursor geöffnet hat. Sie können einen `refcursor`-Wert aus einer gespeicherten Prozedur zurück geben und den Aufrufer mit dem Cursor arbeiten lassen. Alle Portale werden am Transaktionsende implizit geschlossen. Daher können Sie einen `refcursor`-Wert für das Referenzieren eines offenen Cursors nur bis zum Ende der Transaktion verwenden.
+ FETCH überträgt die nächste Zeile aus dem Cursor in ein Ziel. Dieses Ziel kann eine Zeilenvariable, eine Datensatzvariable oder eine durch Kommata getrennte Liste von einfachen Variablen wie bei SELECT INTO sein. Wie bei SELECT INTO können Sie in der speziellen FOUND-Variable überprüfen, ob eine Zeile bezogen wurde.

  ```
  FETCH cursor INTO target;
  ```

  Es folgt ein Beispiel.

  ```
  FETCH curs1 INTO rowvar;
  ```
+ CLOSE schließt das Portal, das einem offenen Cursor zugrunde liegt. Sie können diese Anweisung verwenden, um Ressourcen noch vor dem Ende der Transaktion freizugeben. Sie können diese Anweisung auch verwenden, um die Cursor-Variable zu entblocken, damit sie wieder geöffnet werden kann.

  ```
  CLOSE cursor;
  ```

  Es folgt ein Beispiel.

  ```
  CLOSE curs1;
  ```

## RAISE
<a name="r_PLpgSQL-messages-errors"></a>

Verwenden Sie die `RAISE level`-Anweisung zum Melden von Nachrichten und zum Auslösen von Fehlern.

```
RAISE level 'format' [, variable [, ...]];
```

Mögliche Ebenen sind NOTICE, INFO, LOG, WARNING und EXCEPTION. EXCEPTION meldet einen Fehler, der die aktuelle Transaktion in der Regel abbricht. Die anderen Ebenen generieren nur Nachrichten mit unterschiedlichen Prioritätsstufen. 

Innerhalb der Formatzeichenfolge wird % durch die Zeichenfolgedarstellung des nächsten optionalen Arguments ersetzt. Schreiben Sie %% zum Übermitteln des Literals %. Derzeit müssen optionale Argumente einfache Variablen sein und nicht Ausdrücke, und das Format muss ein einfaches Zeichenfolgeliteral sein.

Im folgenden Beispiel ersetzt der Wert von `v_job_id` das % in der Zeichenfolge.

```
RAISE NOTICE 'Calling cs_create_job(%)', v_job_id;
```

Verwenden Sie die `RAISE`-Anweisung zum erneuten Auslösen der Ausnahme, die vom Ausnahmebehandlungsblock abgefangen wurde. Diese Anweisung ist nur in Ausnahmebehandlungsblöcken von gespeicherten Prozeduren im NONATOMIC-Modus gültig.

```
RAISE;
```

## Transaktionskontrolle
<a name="r_PLpgSQL-transaction-control"></a>

Sie können mit Transaktionskontroll-Anweisungen in der PL/pgSQL-Sprache arbeiten, die Amazon Redshift verwendet. Informationen zur Verwendung von COMMIT-, ROLLBACK- und TRUNCATE-Anweisungen innerhalb eines gespeicherten Verfahrens finden Sie unter [Verwalten von Transaktionen](stored-procedure-transaction-management.md). 

Verwenden Sie für gespeicherte Prozeduren im NONATOMIC-Modus `START TRANSACTION`, um einen Transaktionsblock zu starten.

```
START TRANSACTION;
```

**Anmerkung**  
Die PL/pgSQL-Anweisung START TRANSACTION unterscheidet sich folgendermaßen vom SQL-Befehl START TRANSACTION:  
In gespeicherten Prozeduren ist START TRANSACTION nicht gleichbedeutend mit BEGIN.
Die PL/pgSQL-Anweisung unterstützt keine optionalen Schlüsselwörter für Isolationsstufe und Zugriffsberechtigungen.