Cursor

Ein Cursor ist ein Datenbankobjekt, das als Zeiger auf eine bestimmte Position innerhalb einer Ergebnismenge einer SQL-Abfrage fungiert und die zeilenweise Verarbeitung einzelner Datensätze ermöglicht.

Auf dieser Seite

Ein Cursor ist ein Datenbankobjekt, das als Zeiger auf eine bestimmte Position innerhalb einer Ergebnismenge (Resultset) einer SQL-Abfrage fungiert. Während SQL grundsätzlich mengenorientiert arbeitet und komplette Datensätze auf einmal verarbeitet, ermöglichen Cursor die zeilenweise Verarbeitung einzelner Datensätze, ähnlich wie ein Dateizeiger in der klassischen Programmierung.

Das Konzept ist besonders dann nützlich, wenn du für jeden Datensatz eine individuelle, komplexe Verarbeitung durchführen musst, die sich nicht einfach in einer einzigen SQL-Anweisung ausdrücken lässt. Als Fachinformatiker für Anwendungsentwicklung wirst du Cursor vor allem in Stored Procedures und bei der Implementierung komplexer Geschäftslogik einsetzen.

Warum braucht man Cursor?

Relationale Datenbanken arbeiten nach dem Prinzip der Mengenverarbeitung: Eine SQL-Abfrage gibt alle passenden Zeilen als Gesamtergebnis zurück. Prozedurale Programmiersprachen hingegen arbeiten oft zeilenweise mit Daten. Diese Diskrepanz wird als Impedance Mismatch bezeichnet, und genau hier kommen Cursor ins Spiel.

Typische Anwendungsfälle für Cursor sind:

  • Komplexe Geschäftslogik: Wenn für jeden Datensatz individuelle Berechnungen oder Validierungen nötig sind
  • Zeilenweise Datenverarbeitung: Bei der schrittweisen Verarbeitung großer Datenmengen
  • Interaktive Anwendungen: Wenn Benutzer durch Datensätze navigieren (z.B. Produktlisten)
  • Datenmigrationen: Bei der Transformation von Daten zwischen verschiedenen Systemen
  • Reporting: Für komplexe Berichte, die Zeile für Zeile aufgebaut werden

Der Lebenszyklus eines Cursors

Ein Cursor durchläuft einen klar definierten Lebenszyklus mit fünf Phasen. Das Verständnis dieser Phasen ist grundlegend für die korrekte Arbeit mit Cursorn.

1. DECLARE - Cursor deklarieren

In der ersten Phase wird der Cursor mit einem Namen versehen und die zugehörige SELECT-Abfrage definiert. Hier legst du auch fest, welchen Typ der Cursor haben soll und ob er nur lesend oder auch schreibend auf die Daten zugreifen darf.

2. OPEN - Cursor öffnen

Beim Öffnen wird die SELECT-Abfrage ausgeführt und die Ergebnismenge erstellt. Der Cursor-Zeiger wird vor den ersten Datensatz positioniert. Je nach Cursor-Typ wird das Resultset komplett in den Speicher geladen oder bei Bedarf abgerufen.

3. FETCH - Daten abrufen

Mit FETCH holst du den nächsten (oder bei scrollbaren Cursorn einen beliebigen) Datensatz aus der Ergebnismenge. Die Werte werden in Variablen übertragen, die du dann in deiner Programmlogik verwenden kannst. Der Cursor bewegt sich automatisch zur nächsten Position.

4. CLOSE - Cursor schließen

Nach der Verarbeitung wird der Cursor geschlossen. Dabei wird die Ergebnismenge freigegeben, aber die Cursor-Definition bleibt erhalten. Du kannst den Cursor später erneut öffnen, falls nötig.

5. DEALLOCATE - Ressourcen freigeben

Im letzten Schritt wird die Cursor-Definition vollständig entfernt und alle zugehörigen Ressourcen werden freigegeben. Dies ist wichtig für die Speicherverwaltung und sollte nie vergessen werden.

Cursor-Typen

Datenbanksysteme bieten verschiedene Cursor-Typen mit unterschiedlichen Eigenschaften. Die Wahl des richtigen Typs beeinflusst sowohl die Funktionalität als auch die Performance deiner Anwendung.

Forward-Only Cursor

Der einfachste und effizienteste Cursor-Typ. Er kann nur vorwärts durch die Daten navigieren, vom ersten zum letzten Datensatz. Da kein Scrolling unterstützt wird, verbraucht er minimal Ressourcen. Dieser Typ wird auch Firehose Cursor genannt und ist die beste Wahl für einfache, sequenzielle Verarbeitung.

Static Cursor

Erstellt beim Öffnen eine vollständige Momentaufnahme der Daten. Änderungen an den zugrundeliegenden Tabellen werden nicht sichtbar, der Cursor zeigt immer den Zustand zum Zeitpunkt des Öffnens. Das garantiert Konsistenz, benötigt aber mehr Speicher und kann bei großen Datenmengen langsam sein.

Dynamic Cursor

Das Gegenteil des Static Cursors: Er zeigt immer die aktuellsten Daten an. Alle INSERT-, UPDATE- und DELETE-Operationen anderer Benutzer werden sofort sichtbar. Dies ist ideal für Echtzeitanwendungen, kostet aber mehr Performance wegen der ständigen Synchronisation mit der Datenbank.

Keyset-Driven Cursor

Ein Kompromiss zwischen Static und Dynamic: Beim Öffnen wird ein Keyset (Sammlung von Primärschlüsseln) erstellt. Die eigentlichen Datenwerte werden erst beim FETCH aus der Datenbank geholt. Updates an bestehenden Zeilen sind sichtbar, neue Zeilen (INSERTs) hingegen nicht.

Praktisches Beispiel in T-SQL

Das folgende Beispiel zeigt einen typischen Cursor in Microsoft SQL Server, der alle Datenbanken sichert:

-- Variablen deklarieren
DECLARE @dbName VARCHAR(100)
DECLARE @backupPath VARCHAR(500)

-- Cursor deklarieren
DECLARE db_cursor CURSOR FOR
SELECT name 
FROM sys.databases
WHERE name NOT IN ('tempdb')

-- Cursor oeffnen
OPEN db_cursor

-- Ersten Datensatz holen
FETCH NEXT FROM db_cursor INTO @dbName

-- Schleife durch alle Datensaetze
WHILE @@FETCH_STATUS = 0
BEGIN
    -- Backup-Pfad erstellen
    SET @backupPath = 'C:\Backup\' + @dbName + '.bak'
    
    -- Backup ausfuehren
    BACKUP DATABASE @dbName TO DISK = @backupPath
    
    -- Naechsten Datensatz holen
    FETCH NEXT FROM db_cursor INTO @dbName
END

-- Cursor schliessen und freigeben
CLOSE db_cursor
DEALLOCATE db_cursor

Die Systemvariable @@FETCH_STATUS zeigt den Status der letzten FETCH-Operation an: 0 bedeutet Erfolg, -1 zeigt einen Fehler an, und -2 bedeutet, dass die Zeile nicht mehr existiert.

Cursor in Oracle PL/SQL

Oracle bietet mit PL/SQL eine elegante Cursor-Syntax, besonders die Cursor FOR Loop, die vieles automatisch erledigt:

DECLARE
    -- Cursor deklarieren
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = 10;
BEGIN
    -- Cursor FOR Loop: oeffnet, fetcht und schliesst automatisch
    FOR emp_rec IN emp_cursor LOOP
        -- Fuer jeden Mitarbeiter: Gehalt um 10% erhoehen
        UPDATE employees 
        SET salary = salary * 1.10
        WHERE employee_id = emp_rec.employee_id;
        
        DBMS_OUTPUT.PUT_LINE('Gehaltsanpassung fuer: ' || emp_rec.first_name);
    END LOOP;
    
    COMMIT;
END;

Die Cursor FOR Loop ist in Oracle die bevorzugte Methode, da sie den Code vereinfacht und häufige Fehler wie vergessene CLOSE-Anweisungen vermeidet.

Performance: Cursor vs. mengenbasierte Operationen

Wichtig: Cursor sind in der Regel deutlich langsamer als mengenbasierte SQL-Operationen. Der Grund liegt im wiederholten Kontextwechsel zwischen SQL- und Prozedur-Engine bei jedem FETCH. Ein Vergleich verdeutlicht dies:

Anzahl ZeilenSet-basiertCursor
1005 ms30 ms
10.00010 ms770 ms
100.00020 ms7.600 ms
1.000.000140 ms75 Sekunden

Die Faustregel lautet: Vermeide Cursor, wenn eine mengenbasierte Lösung möglich ist. Ein UPDATE mit WHERE-Klausel ist fast immer schneller als ein Cursor, der jeden Datensatz einzeln aktualisiert.

Optimierungstechnik: BULK COLLECT

Wenn du Cursor verwenden musst, kann die BULK COLLECT-Technik (in Oracle und ähnliche Konstrukte in anderen Systemen) die Performance deutlich verbessern. Statt einzelner FETCHs werden mehrere Zeilen auf einmal geholt:

DECLARE
    TYPE emp_table IS TABLE OF employees%ROWTYPE;
    l_employees emp_table;
    CURSOR emp_cursor IS SELECT * FROM employees;
BEGIN
    OPEN emp_cursor;
    LOOP
        -- 100 Zeilen auf einmal holen
        FETCH emp_cursor BULK COLLECT INTO l_employees LIMIT 100;
        EXIT WHEN emp_cursor%NOTFOUND;
        
        -- Verarbeitung der 100 Zeilen
        FOR i IN 1..l_employees.COUNT LOOP
            -- Geschaeftslogik hier
            NULL;
        END LOOP;
    END LOOP;
    CLOSE emp_cursor;
END;

BULK COLLECT kann die Performance um den Faktor 3–5 verbessern, indem es die Anzahl der Kontextwechsel reduziert.

Wann solltest du Cursor verwenden?

Trotz der Performance-Nachteile gibt es legitime Anwendungsfälle für Cursor:

  • Komplexe zeilenweise Logik: Wenn jede Zeile unterschiedlich behandelt werden muss und dies nicht in SQL ausdrückbar ist
  • Externe Aufrufe: Wenn für jeden Datensatz ein externes System aufgerufen werden muss (z.B. API-Calls)
  • Schrittweise Commits: Bei sehr großen Datenmengen, um Transaktions-Logs nicht zu überlasten
  • Debugging und Protokollierung: Wenn der Verarbeitungsfortschritt nachverfolgt werden soll
  • Administrative Aufgaben: Wie im Backup-Beispiel, wo für jede Datenbank ein separater Befehl nötig ist

Cursor in verschiedenen Datenbanksystemen

Die grundlegende Funktionsweise von Cursorn ist ähnlich, aber jedes Datenbanksystem hat seine Besonderheiten:

DatenbanksystemBesonderheiten
SQL ServerVolle Unterstützung aller Cursor-Typen, @@FETCH_STATUS für Statusabfrage
OracleCursor FOR Loop, BULK COLLECT, implizite Cursor für SELECT INTO
PostgreSQLCursor via refcursor-Variablen in PL/pgSQL
MySQLNur in Stored Procedures, nur Forward-Only, Read-Only

MySQL hat die stärksten Einschränkungen: Cursor funktionieren nur in Stored Procedures, sind nicht scrollbar und erlauben keine Datenmodifikation. Für komplexere Anforderungen musst du hier oft auf Anwendungslogik ausweichen.

Cursor in der IHK-Prüfung

Für die Abschlussprüfung zum Fachinformatiker solltest du folgende Punkte beherrschen:

  • Die fünf Phasen des Cursor-Lebenszyklus (DECLARE, OPEN, FETCH, CLOSE, DEALLOCATE)
  • Die Unterschiede zwischen Forward-Only, Static, Dynamic und Keyset-Driven Cursorn
  • Wann Cursor sinnvoll sind und wann mengenbasierte Operationen bevorzugt werden sollten
  • Grundlegende Syntax in mindestens einem SQL-Dialekt (meist T-SQL oder PL/SQL)
  • Das Konzept des Impedance Mismatch zwischen SQL und prozeduralen Sprachen

Prüfungsbezug

Passt zu deiner Prüfungsvorbereitung

Wenn dir dieser Begriff in Aufgaben, Projektdokumentation oder Fachgespräch begegnet, ordne ihn direkt in den Prüfungsstoff ein.