Зачем использовать явные курсоры вместо обычных циклов?

12

Я писал базовые веб-приложения в течение года (для базы данных Oracle), и, поскольку функции довольно просты, большинство из нас используют обычные циклы FOR для получения наших данных:

for i in (select * from STUDENTS) loop
      htp.prn(i.student_last_name || ', ' || i.student_first_name || ' ' || i.student_dob);
end loop;

Но курсоры кажутся «правильным» способом сделать что-то. Я могу найти много информации о том, что такое курсоры, и о различных способах их обхода, но не могу найти вескую причину, по которой их можно использовать поверх обычных циклов FOR. Это зависит от потребностей процедуры? Есть ли присущие мне преимущества, о которых я должен знать?

ини
источник
Этот тип FORпросто еще один способ использовать курсоры. Смотрите документы: docs.oracle.com/cd/E11882_01/appdev.112/e10472/… В любом случае, что делает htp.prn ()?
Дезсо
Это одна из наших функций вывода. Итак, у вас есть предпочтения, какой метод использовать?
Ини
FORЯ думаю, что для такого рода вещей цикл гораздо более читабелен. Я склонен использовать «настоящие» курсоры, только если мне нужно сделать шаг назад, а не только вперед. Я задал этот другой вопрос, потому что я могу представить табличную функцию вместо htp.prn().
Дезсо
Стоит отметить, что обе формы курсора имеют более низкую производительность по сравнению с решением на чистом SQL, что особенно важно для операторов DML.
Дэвид Олдридж

Ответы:

7

Курсор может быть явным или неявным, и любой тип может использоваться в цикле FOR. На самом деле есть два аспекта вашего вопроса.

  1. Зачем использовать явный курсорный цикл FOR над неявным курсорным циклом FOR?

    • Используйте явный курсор для цикла FOR, когда запрос будет использоваться повторно, в противном случае предпочтителен неявный курсор.
  2. Зачем использовать цикл с FETCH, а не цикл FOR, который не имеет явного FETCH?

    • Используйте FETCH внутри цикла, когда вам нужно выполнить массовый сбор или когда вам нужен динамический SQL.

Вот некоторая полезная информация из документации.

Пример неявного курсора FOR LOOP

BEGIN
   FOR vItems IN (
      SELECT last_name
      FROM employees
      WHERE manager_id > 120
      ORDER BY last_name
   ) 
   LOOP
      DBMS_OUTPUT.PUT_LINE ('Name = ' || vItems.last_name);
   END LOOP;
END;
/

Пример явного курсора для цикла

DECLARE
   CURSOR c1 IS
      SELECT last_name
      FROM employees
      WHERE manager_id > 120
      ORDER BY last_name;
BEGIN
   FOR vItems IN c1 LOOP
      DBMS_OUTPUT.PUT_LINE ('Name = ' || vItems.last_name);
   END LOOP;
END;
/

Неявный курсор

Неявный курсор - это курсор сеанса, который создается и управляется PL / SQL. PL / SQL открывает неявный курсор каждый раз, когда вы запускаете оператор SELECT или DML. Вы не можете управлять неявным курсором, но вы можете получить информацию из его атрибутов.

Неявный курсор закрывается после выполнения соответствующего оператора; однако значения его атрибутов остаются доступными до выполнения другого оператора SELECT или DML.

Неявные атрибуты курсора: SQL% ISOPEN, SQL% FOUND, SQL% NOTFOUND, SQL% ROWCOUNT, SQL% BULK_ROWCOUNT, SQL% BULK_EXCEPTIONS

Явный курсор

Явный курсор - это курсор сеанса, который вы создаете и управляете им. Вы должны объявить и определить явный курсор, присвоив ему имя и связав его с запросом (обычно запрос возвращает несколько строк). Затем вы можете обработать набор результатов запроса одним из следующих способов:

Откройте явный курсор (с помощью оператора OPEN), извлеките строки из результирующего набора (с помощью оператора FETCH) и закройте явный курсор (с помощью оператора CLOSE).

Используйте явный курсор в операторе курсора FOR LOOP (см. «Обработка набора результатов запроса с помощью операторов Cursor FOR LOOP»).

Вы не можете присвоить значение явному курсору, использовать его в выражении или использовать в качестве формального параметра подпрограммы или переменной хоста. Вы можете сделать это с помощью переменной курсора (см. «Переменные курсора»).

В отличие от неявного курсора, вы можете ссылаться на явный курсор или переменную курсора по его имени. Поэтому явный курсор или переменная курсора называется именованным курсором.

Курсор для операторов LOOP

Оператор курсора FOR LOOP позволяет запустить инструкцию SELECT, а затем сразу же выполнить цикл по строкам набора результатов. Этот оператор может использовать неявный или явный курсор.

Ли Риффель
источник
1
Неявные курсоры извлекают 100 строк одновременно, начиная с 10g.
Дэвид Олдридж
16

Код, который вы разместили, использует курсор. Это использует неявный цикл курсора.

Существуют случаи, когда использование явного цикла курсора (т. Е. Объявление переменной CURSOR в разделе объявлений) дает либо более чистый код, либо лучшую производительность

  1. Если у вас есть более сложные запросы, которые вы не можете преобразовать в представления, это может облегчить чтение кода, если ваш цикл повторяется student_cursorвместо включения 30-строчного SQL-оператора, в который встроена куча логики. Например, если вы распечатывали всех студентов, которые были допущены к выпуску и которые включали в себя присоединение к таблицам, в которых были свои академические записи, требования к программе их степеней, таблицы с информацией об академических трюмах, таблицы с информацией о просроченных библиотечных книгах, таблицы с информацией о невыплаченных сборах, административных переопределениях и т. д., вероятно, имеет смысл реорганизовать код, чтобы этот запрос не застрял в середине кода, который связан с представлением списка пользователю. Это может включать создание представления, которое инкапсулирует всю эту логику. Или это может включать создание явного курсора, который был объявлен либо как часть текущего блока PL / SQL, либо в каком-либо блоке PL / SQL более высокого уровня (т.е. курсор, объявленный в пакете), поэтому его можно использовать повторно. Или это может включать в себя что-то еще для инкапсуляции и повторного использования (скажем, вместо этого создание конвейерной табличной функции).
  2. Если вы хотите использовать массовые операции в PL / SQL, вы, как правило, хотите использовать явные курсоры. Вот поток StackOverflow, в котором обсуждаются различия в производительности между явными и неявными курсорами . Если все, что вы делаете, это звоните htp.prn, то, BULK COLLECTвероятно, ничего не купит. Однако в других случаях это может привести к значительному повышению производительности.
Джастин Кейв
источник
2

Я вижу, что многие разработчики используют явные курсоры вместо неявных курсоров по старой привычке. Это потому, что в Oracle версии 7 это всегда был более эффективный путь. В наши дни, как правило, все наоборот. Специально с оптимизатором, который при необходимости может переписать неявный курсор для циклов для массового сбора.

Питер Акерлунд
источник
0

Недавно мне пришлось переписать несколько запросов из неявного цикла FOR в явные курсоры. Причина была в том, что запросы извлекали данные из внешней базы данных по ссылке, и эта база данных отличалась от нашей локальной базы данных. При переносе данных из неявного курсора в локально определенный тип записи возникали загадочные периодические ошибки (только в определенных конкретных строках). Наш DBA объяснил нам это, мы бы не смогли разобраться в этом сами. Кажется, это ошибка в Oracle, о которой сообщалось.

Нам посоветовали переписать все, используя явные курсоры, и ошибка исчезла.

Не основная причина, по которой вы можете использовать явное, а не явное, но стоит обратить внимание.

РЕДАКТИРОВАТЬ: Oracle 12c.

Robotron
источник
Не могли бы вы добавить номер ошибки и / или примечания, чтобы те, кто читает это, могли больше узнать о симптомах и, если / когда это будет решено?
Ли Риффель
Извините, сообщение об ошибке было сделано одним из наших администраторов баз данных, у меня нет доступа к этой информации.
Роботрон