Einfache Cursor

Cursor können nur innerhalb einer Transaktion deklariert werden:

test=# DECLARE mycursor CURSOR FOR SELECT * FROM t_passwd;
ERROR:  DECLARE CURSOR may only be used in transaction blocks

Mit Transaktionen werden wir uns noch in einem eigenen Kapitel beschäftigen - belassen wir es derweilen einmal dabei, dass wir vor einem Cursor ein BEGIN benötigen:

test=# BEGIN;
BEGIN
test=# DECLARE mycursor CURSOR FOR SELECT * FROM t_passwd;
DECLARE CURSOR

Ist der Cursor erst einmal deklariert, können wir die Ergebnismenge mit Hilfe von FETCH abfragen:

test=# \h FETCH
Command:     FETCH
Description: retrieve rows from a query using a cursor
Syntax:
FETCH [ direction { FROM | IN } ] cursorname

where direction can be empty or one of:

    NEXT
    PRIOR
    FIRST
    LAST
    ABSOLUTE count
    RELATIVE count
    count
    ALL
    FORWARD
    FORWARD count
    FORWARD ALL
    BACKWARD
    BACKWARD count
    BACKWARD ALL

Im einfachsten Fall wollen Sie schlichtweg einen Datensatz nach dem Anderen selektieren:

test=# FETCH NEXT FROM mycursor;
 account | passwd | uid | gid | gecos | directory |   shell
---------+--------+-----+-----+-------+-----------+-----------
 root    | x      |   0 |   0 | root  | /root     | /bin/bash
(1 row)

test=# FETCH NEXT FROM mycursor;
 account | passwd | uid | gid | gecos | directory |     shell
---------+--------+-----+-----+-------+-----------+---------------
 bin     | x      |   1 |   1 | bin   | /bin      | /sbin/nologin
(1 row)

Obwohl wir eine (möglicherweise) große Datenmenge durchlaufen, wird immer nur ein Datensatz auf einmal im Speicher gehalten, da die Datenbank das Ergebnis erst durch den Aufruf von FETCH berechnet.

Da das Abfragen von einem Datensatz eher ineffizient ist, kann man sich auch entscheiden, ein Bündel von Datensätzen auf einmal berechnen zu lassen. Das nächste Beispiel zeigt, wie drei Datensätze auf einmal erzeugt werden können:

test=# FETCH 3 FROM mycursor;
 account | passwd | uid | gid | gecos  |   directory    |     shell
---------+--------+-----+-----+--------+----------------+---------------
 daemon  | x      |   2 |   2 | daemon | /sbin          | /sbin/nologin
 adm     | x      |   3 |   4 | adm    | /var/adm       | /sbin/nologin
 lp      | x      |   4 |   7 | lp     | /var/spool/lpd | /sbin/nologin
(3 rows)

In der Regel fragt man Datensätze immer in Blöcken ab (beispielsweise 1000 Datensätze), da man so beträchtlichen Kommunikationsoverhead sparen kann. Die Größe dieser Datenpackages sollten Sie von Ihrer Applikation abhängig machen.

Cursor bieten auch die Möglichkeit, im Ergebnis zu springen. Sie können jederzeit auf vorangegangene Datensätze zurückgreifen:

test=# FETCH PRIOR FROM mycursor;
 account | passwd | uid | gid | gecos | directory |     shell
---------+--------+-----+-----+-------+-----------+---------------
 adm     | x      |   3 |   4 | adm   | /var/adm  | /sbin/nologin
(1 row)

test=# FETCH PRIOR FROM mycursor;
 account | passwd | uid | gid | gecos  | directory |     shell
---------+--------+-----+-----+--------+-----------+---------------
 daemon  | x      |   2 |   2 | daemon | /sbin     | /sbin/nologin
(1 row)

Durch Verwendung des Schlüsselwortes PRIOR wird PostgreSQL aufgefordert, die vorangegangenen Datensätze zu durchlaufen.

Auch der Befehl MOVE dient zur Positionierung innerhalb des Cursors:

test=# \h MOVE
Command:     MOVE
Description: position a cursor
Syntax:
MOVE [ direction { FROM | IN } ] cursorname

Wollen Sie einen Cursor wieder beenden, benötigen Sie den Befehl CLOSE:

test=# \h CLOSE
Command:     CLOSE
Description: close a cursor
Syntax:
CLOSE name

Ansich reicht ein einfaches ROLLBACK respektive ein einfaches COMMIT, um einen Cursor zu beenden - wir empfehlen allerdings trotzdem, den Cursor korrekt zu schließen und anschließend die Transaktion zu beenden:

test=# CLOSE mycursor;
CLOSE CURSOR
test=# ROLLBACK;
ROLLBACK


Cybertec Schönig & Schönig GmbH
PostgreSQL support, training, consulting
www.postgresql-support.de