Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Was ist falsch an Cursors?

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 auf SUM 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.