Ce qui ne va pas avec les curseurs, c'est qu'ils sont souvent abusés, à la fois dans Oracle
et en MS SQL
.
Le curseur sert à conserver un jeu de résultats stable que vous pouvez récupérer ligne par ligne. Ils sont implicitement créés lors de l'exécution de votre requête et fermés lorsqu'elle est terminée.
Bien sûr, conserver un tel jeu de résultats nécessite certaines ressources :locks
, latches
, memory
, voire disk space
.
Plus ces ressources sont libérées rapidement, mieux c'est.
Laisser un curseur ouvert, c'est comme garder la porte d'un réfrigérateur ouverte
Vous ne le faites pas pendant des heures sans nécessité, mais cela ne signifie pas que vous ne devriez jamais ouvrir votre réfrigérateur.
Cela signifie que :
- Vous n'obtenez pas vos résultats ligne par ligne et ne les additionnez pas :vous appelez le
SQL
estSUM
à la place. - Vous n'exécutez pas la requête entière et n'obtenez pas les premiers résultats du curseur :vous ajoutez un
rownum <= 10
condition à votre requête
, etc.
Comme pour Oracle
, le traitement de vos curseurs à l'intérieur d'une procédure nécessite le fameux SQL/PLSQL context switch
qui se produit à chaque fois que vous obtenez un résultat d'un SQL
requête hors du curseur.
Cela implique de transmettre de grandes quantités de données entre les threads et de synchroniser les threads.
C'est l'une des choses les plus irritantes dans Oracle
.
L'une des conséquences les moins évidentes de ce comportement est que les déclencheurs dans Oracle doivent être évités si possible.
Création d'un déclencheur et appel d'un DML
fonction revient à ouvrir le curseur en sélectionnant les lignes mises à jour et en appelant le code déclencheur pour chaque ligne de ce curseur.
La simple existence du déclencheur (même le déclencheur vide) peut ralentir un DML
opération 10 times
ou plus.
Un script de test sur 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
secondes sans déclencheur, 17.57
secondes avec un déclencheur vide ne faisant rien.