Изменения

Перейти к: навигация, поиск

Курсоры

5932 байта добавлено, 00:04, 27 декабря 2021
Добавлено больше текста
'''Курсор''' — способ перебора результатов запроса, позволяющий достать из результата запроса множество строк.}}
Курсор можно рассматривать как итератор, специфичный для баз данных.
 
Запрос исполнился, мы получили какие-то результаты, и теперь собираемся достать множество строк. Все, что обсуждалось до этого (set и select into) позволяло доставать только один результат, а из курсора их можно достать множество.
== Объявление курсора ==
[[<font color = black>no</font>] <font color = black>scroll</font>]</font><font color = green> -- возможность ходить произвольным образом по данным вперед и назад (default - no scroll) </font>
cursor select<font color = gray>...</font><font color = green> -- запрос данных, по которым будет ходить курсор</font>
<font color = red>[</font>for <font color = red>{</font>read <font color = black>only</font><font color = gray>|</font>update <font color = red>[</font>of <font color = red>столбцы]}]</font></font><font color = gray>;</font> <font color = green> -- права на чтение(дефолтное право) или на обновление (всей таблицы (в частности, этот вариант подходит для удаления) или выбранных столбцов)</font>
=== Чувствительность курсора ===
Данные из курсора обрабатываются по одной строке(способа получить более одной строки за один раз нет). Мы напишем цикл, в рамках которого будут выбираться данные из курсора, и возможно будут меняться данные в нашей базе данных. Получаем, что во время таких итераций можно поменять данные самой таблицы, по которой построен текущий курсор. Представим ситуацию, что мы в сделанном курсоре в явной форме написали update на ту таблицу или таблицы, по которым сделан курсор. Что должно произойти в таком случае? В зависимости от желаемого поведения в таких случаях можно выбрать разную чувствительность курсора.
* <tex>\mathtt{sensitive}</tex> - курсор, чувствительный к изменениям. Измененные данные можно будет увидеть в курсоре, если эти данные еще не были пройдены. Иначе, если изменённые данные были просмотрены в прошломи у нас не scrollable курсор, то они не обновятся. Итерация В некотором смысле это итерация по реальным данным, динамическая.* <tex>\mathtt{insensitive}</tex> - курсор фиксированных данных. Данные зафиксированы на момент открытия курсора. Никакие изменения данных не будут отображены через курсор. Итерация В некотором смысле это итерация по временной копии таблицы.* <tex>\mathtt{asensitive}</tex> - курсор, наиболее предпочтительный для данной СУБД. Связано с различными способами реализациями СУБД: для одних легче реализуем sensitive cursor, а для других - insensitive. В данном случае Существует два подхода к реализации СУБД: snapshot-based и lock-based, для них либо sensitive, либо insensitive варианты предпочтительны. Ни одна из опций не является дешевой по умолчанию, поэтому и есть значение asensitive: сама СУБД данных будет решать, какую чувствительность курсора использовать. Для lock-based проще сделать курсор, чувствительный к изменениям, так как для insensitive потребовалось бы дополнительное копирование. Для snapshot-based предпочтительнее insensitive, так как не потребуется специальным образом учитывать обновления.
Если данные во время использования курсора меняться не будут, то чувствительность курсора не важна.
 
=== Scrollable | No scroll курсоры ===
 
* <tex>\mathtt{No}</tex> <tex>\mathtt{scroll}</tex> - по такому курсору можно ходить только по одной записи вперед, как из обычного итератора в Java.
* <tex>\mathtt{Scroll}</tex> - по такому курсору можно ходить не только по одной записи вперед, но и вперед, назад, в начало или конец. Такой курсор больше похож на итератор из языка С++.
===Примеры===
Взятие курсора на чтение для всех студентов, упорядоченных по идентификатору:
<font color = blue>declare <font color = black>SCursor</font> cursor
select <font color = gray>*</font> from <font color = black>Students</font>
order by <font color = black>SId</font><font color = gray>;</font></font>
Взятие курсора с правом на изменение поля SName для студентов группы M3439в произвольном порядке:
<font color = blue>declare <font color = black>SCursor</font> cursor
select <font color = gray>*</font> from <font color = black>Students</font> where <font color = black>GId</font> <font color = gray>=</font> <font color = green>'M3439'</font>
==Открытие и закрытие==
Объявление курсора говорит о намерении получения данных. Реальная выборка в момент объявления не производится. Она происходит в момент открытия курсора. Если курсор ''insensitive'', то в момент открытия фиксируются данные, которые попадут в курсор.
Объявление курсора говорит о намерении получения данных. Реальная выборка в момент объявления не производится. Она происходит в момент открытия курсора (выполнения действия open cursor). Если курсор ''insensitive'', то в момент открытия фиксируются данные, которые попадут в курсор. С курсором ассоциируются ресурсымножество ресурсов, поэтому их необходимо закрывать. При выходе из блока, в котором был объявлен курсор, он будет автоматически закрыт. Это чем-то напоминает автоматическую сборку ресурсов в sql. При повторном открытии курсора произойдет повторная инициализация, влекущая новое прочтение данных для ''insensitive'' курсора.
=== Синтаксис ===
== Получение данных ==
 
Для получения данных из курсора есть конструкция fetch.
=== Синтаксис ===
* <tex>\mathtt{first}</tex> - первая запись
* <tex>\mathtt{absolute}</tex> <tex>\mathtt{n}</tex> - получить запись с номером n от начала
* <tex>\mathtt{relative}</tex> <tex>\mathtt{n}</tex> - запись с номером n, нумерация с текущей позиции (n может быть как положительным (движение в сторону конца), так и отрицательным, (в этом случае движение назад))
=== Отсутствие данных ===
 Если данные закончились, либо при помощи команд выше был осуществлен невалидный переход, то случится событие '''not found'''.
=== Пример ===
 
В данном примере из курсора для студентов мы получим идентификатор, имя и фамилию того студента, на которого сейчас указывает курсор.
 
<font color = blue>fetch </font><font color = black>SCursor <font color = blue>into </font>Sid<font color = gray>,</font> FirstName<font color = gray>,</font> LastName<font color = gray>;</font></font>
Для получения всех данных из курсора, можно создать цикл, предварительно создав обработчик ошибок на '''not found'''снаружи цикла, в котором выйдем из цикла, когда все элементы будут обработаны. Состояние '''sqlstate 02xxx''' не является исключительной ситуацией, а просто предупреждение от СУБД, которое зачастую игнорируется. Вопрос производительности зависит от реализации СУБД.
== Позиционированные изменения ==
 Изменение или удаление ровно одной строчки, которую последней выдал курсор(только в <code>update</code> и <code>delete</code>)
=== Синтаксис ===
  <font color = blue>update <font color = red>таблица</font> set <font color = red>что</font> where current of <font color = red>курсор</font><font color = gray>;</font></font> <font color = green> -- изменение ровно одной строки, которую последней выдал курсор </font> <font color = blue>delete from <font color = red>таблица</font> where current of <font color = red>курсор</font><font color = gray>;</font></font><font color = green> -- удаление последней выданной строки </font> 
===Пример===
<font color = blue>if <font color = black>FirstName</font> <font color = gray>=</font> <font color = green>'Иван'</font> then
delete from <font color = black>Students</font> where current of <font color = black>SCursor</font><font color = gray>;</font>
end if<font color = gray>;</font></font> В данном примере мы удалим текущего студента, если его имя Иван. 
== Пример применения курсора ==
<font color = blue>create procedure <font color = black>updateNameStat<font color = gray>(</font>name<font color = gray>)</font></font>
begin
declare <font color = black>cnt int</font> default <font color = darkgreen>0</font><font color = gray>;</font>
=== Пояснения ===
В данном примере объявлен счетчик <code>cnt</code>, по умолчанию проинициализированный 0, курсор <code>SCursor</code>, перебирающий значение столбца <code>FirstName</code> из таблицы <code>Students</code>. Далее в бесконечном цикле(так как мы не имеет возможности написать условие выхода из цикла):
# определяем переменную <code>sname</code>, в которой будем хранить имя студента
# достаем следующую запись в эту переменную (напоминание: <code>fetch</code> по умолчанию - это <code>fetchnext</code> next)
# проверяем, если текущее имя <code>sname</code> имя соответствует выделенному имени <code>name</code>, то увеличиваем счетчик.
Повторяем данные операции до тех пор, пока у нас не случится <code>not found</code>, при возникновении которого в обработчике <code>not found</code> обновим данные статистики, и (по определению обработчика в sql) выйдем из блока, в котором был объявлен <code>handler</code> - то есть в данном случае выйдем из блока процедуры. Обработчик <code>not found</code> может быть только один. Если бы у нас было несколько курсоров, и мы бы захотели знать в обработчике, в каком из курсоров закончились данные, то предварительно вели бы записи в переменные, из каких курсоров планируем достать данные. И в обработчике проверили бы значение переменных. Данные пример написан исключительно для демонстрации работы с курсорами. В реальной жизни подобное поведение может быть достигнут проще с использованием <code>select</code> и <code>count</code>.
===== Примечания =====
==Особенности курсоров==
 
Курсоры, описанные в данной статье, являются '''внутренними'''. Они существуют внутри базы данных, используются, в том числе для взаимодействия между различными частями базы данных при планировании.
===Внешние курсоры===
 Результат запроса к базе данных может быть слишком велик и стать виной нехватки памяти как на стороне клиента, так и на стороны сервера, увеличить нагрузку на сеть, вызывать и другие проблемы. Поэтому наружу данные выдаются в виде внешнего курсора. После исполнения команды select выдается внешний курсор на данные, аналогично внешний курсор выдается при возврате результата из хранимой процедуры.
===Размер выборки===
Может быть указан для внешнего курсора каким-либо образом (в количестве записей или в объеме данных, который выдается за один раз).
При выдаче курсора приходит метаинформация о курсоре и первая порция данных, размер которой мы указали. Для получения последующих порций данных необходимо обращаться к серверу, что будет медленнее, чем получение первой порции. В конце сервер сообщит о том, что данные закончились, автоматически освободит ресурсы.
Возникают альтернативы:
* Малый размер выборки — много повторных обращенийк серверу базы данных, а так как обычно такое обращение происходит по сети, то взаимодействие не слишком быстрое.* Большой размер выборки — требуется много памяти, как со стороны сервера для генерации данных, так и со стороны прикладного приложения.
Выдача курсора приводит к расходу ресурсов сервера (память, возможно дисковая память, блокировки и так далее). Поэтому внешний курсор стоит закрыть как можно раньше. Однако чтобы не получить слишком много данных по запросу, они выдаются в виде внешнего курсора.
==Литература==
9
правок

Навигация