Что плохо с курсорами, так это то, что ими часто злоупотребляют как в Oracle
, так и в MS SQL
.
Курсор для сохранения стабильного набора результатов, который вы можете извлекать построчно. Они неявно создаются при выполнении запроса и закрываются по его завершении.
Конечно, для сохранения такого набора результатов требуются некоторые ресурсы: locks
, latches
, memory
, даже disk space
.
Чем быстрее освободятся эти ресурсы, тем лучше.
Держать курсор открытым - это все равно, что держать дверь холодильника открытой
Вы не делаете это часами без необходимости, но это не значит, что вы никогда не должны открывать свой холодильник.
Это означает, что:
- Вы не получаете свои результаты построчно и не суммируете их: вместо этого вы вызываете
SQL
SUM
.
- Вы не выполняете весь запрос и получаете первые результаты от курсора: вы добавляете условие
rownum <= 10
к вашему запросу
и т. Д.
Что касается Oracle
, обработка ваших курсоров внутри процедуры требует печально известного SQL/PLSQL context switch
, что происходит каждый раз, когда вы получаете результат запроса SQL
из курсора.
Включает передачу больших объемов данных между потоками и синхронизацию потоков.
Это одна из самых раздражающих вещей в Oracle
.
Одним из менее очевидных последствий такого поведения является то, что по возможности следует избегать триггеров в Oracle.
Создание триггера и вызов функции DML
равны открытию курсора, выбору обновленных строк и вызову кода триггера для каждой строки этого курсора.
Само существование триггера (даже пустой триггер) может замедлить DML
операцию 10 times
или более.
Тестовый скрипт на 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
секунд без триггера, 17.57
секунд с пустым триггером, ничего не делающим.