Co jest nie tak z kursorami to to, że są one często nadużywane, zarówno w Oracle
oraz w MS SQL
.
Kursor służy do utrzymywania stabilnego zestawu wyników, który można pobierać wiersz po wierszu. Są one tworzone niejawnie po uruchomieniu zapytania i zamykane po jego zakończeniu.
Oczywiście utrzymanie takiego zestawu wyników wymaga pewnych zasobów:locks
, latches
, memory
, nawet disk space
.
Im szybciej te zasoby zostaną zwolnione, tym lepiej.
Utrzymywanie otwartego kursora jest jak otwieranie drzwi lodówki
Nie robisz tego godzinami bez konieczności, ale to nie znaczy, że nigdy nie powinieneś otwierać lodówki.
Oznacza to, że:
- Nie otrzymujesz wyników wiersz po wierszu i nie sumujesz ich:wywołujesz
SQL
SUM
zamiast tego. - Nie wykonujesz całego zapytania i otrzymujesz pierwsze wyniki z kursora:dołączasz
rownum <= 10
warunek zapytania
itp.
Co do Oracle
, przetwarzanie kursorów wewnątrz procedury wymaga niesławnego SQL/PLSQL context switch
co dzieje się za każdym razem, gdy otrzymujesz wynik SQL
zapytanie poza kursorem.
Polega na przesyłaniu dużych ilości danych między wątkami i synchronizowaniu wątków.
To jedna z najbardziej irytujących rzeczy w Oracle
.
Jedną z mniej oczywistych konsekwencji takiego zachowania jest to, że w miarę możliwości należy unikać wyzwalaczy w Oracle.
Tworzenie wyzwalacza i wywoływanie DML
funkcja jest równa otwarciu kursora, wybraniu zaktualizowanych wierszy i wywołaniu kodu wyzwalającego dla każdego wiersza tego kursora.
Samo istnienie wyzwalacza (nawet pustego wyzwalacza) może spowolnić DML
operacja 10 times
lub więcej.
Skrypt testowy na 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
sekundy bez wyzwalacza, 17.57
sekundy z pustym spustem, który nic nie robi.