Was mit Cursorn nicht stimmt, ist, dass sie oft missbraucht werden, sowohl in Oracle
und in MS SQL
.
Cursor dienen dazu, eine stabile Ergebnismenge zu halten, die Sie Zeile für Zeile abrufen können. Sie werden implizit erstellt, wenn Ihre Abfrage ausgeführt wird, und geschlossen, wenn sie beendet ist.
Natürlich erfordert das Aufrechterhalten einer solchen Ergebnismenge einige Ressourcen:locks
, latches
, memory
, sogar disk space
.
Je schneller diese Ressourcen freigegeben werden, desto besser.
Einen Cursor offen zu halten ist wie eine Kühlschranktür offen zu halten
Du machst es nicht stundenlang ohne Notwendigkeit, aber das bedeutet nicht, dass du niemals deinen Kühlschrank öffnen solltest.
Das bedeutet:
- Sie erhalten Ihre Ergebnisse nicht Zeile für Zeile und summieren sie:Sie rufen
SQL
aufSUM
von stattdessen. - Sie führen nicht die ganze Abfrage aus und erhalten die ersten Ergebnisse vom Cursor:Sie hängen eine
rownum <= 10
an Bedingung für Ihre Anfrage
usw.
Wie bei Oracle
, erfordert die Verarbeitung Ihrer Cursor innerhalb einer Prozedur den berüchtigten SQL/PLSQL context switch
was jedes Mal passiert, wenn Sie ein Ergebnis von SQL
erhalten Abfrage aus dem Cursor heraus.
Dabei werden große Datenmengen zwischen Threads übertragen und die Threads synchronisiert.
Dies ist eines der irritierendsten Dinge in Oracle
.
Eine der weniger offensichtlichen Folgen dieses Verhaltens ist, dass Trigger in Oracle nach Möglichkeit vermieden werden sollten.
Erstellen eines Triggers und Aufrufen einer DML
Funktion entspricht dem Öffnen des Cursors, der die aktualisierten Zeilen auswählt und den Triggercode für jede Zeile dieses Cursors aufruft.
Die bloße Existenz des Triggers (sogar des leeren Triggers) kann eine DML
verlangsamen Vorgang 10 times
oder mehr.
Ein Testskript auf 10g
:
SQL> CREATE TABLE trigger_test (id INT NOT NULL)
2 /
Table created
Executed in 0,031 seconds
SQL> INSERT
2 INTO trigger_test
3 SELECT level
4 FROM dual
5 CONNECT BY
6 level <= 1000000
7 /
1000000 rows inserted
Executed in 1,469 seconds
SQL> COMMIT
2 /
Commit complete
Executed in 0 seconds
SQL> TRUNCATE TABLE trigger_test
2 /
Table truncated
Executed in 3 seconds
SQL> CREATE TRIGGER trg_test_ai
2 AFTER INSERT
3 ON trigger_test
4 FOR EACH ROW
5 BEGIN
6 NULL;
7 END;
8 /
Trigger created
Executed in 0,094 seconds
SQL> INSERT
2 INTO trigger_test
3 SELECT level
4 FROM dual
5 CONNECT BY
6 level <= 1000000
7 /
1000000 rows inserted
Executed in 17,578 seconds
1.47
Sekunden ohne Trigger, 17.57
Sekunden mit einem leeren Trigger, der nichts tut.