Sources
Delphi Russian Knowledge Base
DRKB - база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Курсоры: доступ к отдельным строкам

01.01.2007

Оператор выбора (select) возвращает обычно несколько строк, либо ничего не возвращает. Если оператор выбора возвращает в результате несколько строк, то с помощью курсоров можно получить доступ к каждой строке в отдельности.

В этой главе рассматриваются следующие темы:

Что такое курсор ?

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

Позиция курсора указывает на текущую строку курсора. Пользователь может непосредственно модифицировать или удалять эту строку операторами update или delete, используя конструкцию с названием курсора. Можно изменить текущую позицию курсора с помощью операции fetch (передвинуть и загрузить). Эта операция переводит курсор на одну или несколько строк ниже в результирующем множестве.

Курсор аналогичен указателю на записи файла. Однако курсор можно сдвигать только вперед по результирующему множеству (последовательный доступ). Если несколько строк уже пройдены, то нельзя вернуться назад и снова получить к ним доступ, переведя на них курсор. Этот процесс позволяет просматривать строки друг за другом.

После объявления курсора он может находиться в двух состояниях:

Курсор можно закрыть и затем снова отрыть его. Повторное открытие курсора вновь создает результирующее множество и курсор устанавливается непосредственно перед первой строкой в этом множестве. Это позволяет пройти по результирующему множеству столько раз, сколько это необходимо. Курсор можно закрыть в любое время; необязательно проходить все результирующее множество.

Все операции с курсором, такие как передвижение и модификация, выполняются по отношению к текущей позиции курсора. Обновление курсорной строки включает в себя изменение данных в этой строке и полное удаление этой строки. Курсор нельзя использовать для вставки строк. Все обновления через курсор выполняются в соответствующих базовых таблицах, откуда выбирается результирующее множество.

Как SQL Сервер обрабатывает курсоры

Когда доступ к данным осуществляется через курсор, SQL Сервер разбивает процесс на следующие шаги:

Объявление курсоров

Пользователь должен объявить курсор прежде, чем можно будет его использовать. В объявлении указывается запрос, который определяет результирующее множество курсора. Пользователь может явно объявить курсор для обновления или только для чтения с помощью ключевых слов for update (для обновления) или for read only (только для чтения). Если эти слова не указаны, то SQL Сервер определяет можно ли обновлять через курсор, основываясь на типе запроса, который формирует результирующее множество курсора. Нельзя использовать операторы update и delete в формирующем запросе для курсора, объявленного только для чтения.

Синтаксис объявления курсора

Синтаксис оператора declare cursor (объявление курсора) имеет следующий вид:

declare название_курсора cursor
  for оператор_выбора
  [for {read only | update [of список_столбцов]}]

Оператор declare cursor должен предшествовать любому оператору open (открыть) для этого курсора. Оператор declare cursor нельзя совмещать с другими операторами в одном Transact-SQL пакете за исключением случая, когда курсор используется в сохраненной процедуре.

Оператор_выбора (select) является запросом, который определяет результирующее множество данного курсора. Вообще говоря, в этом операторе можно использовать весь синтаксис и семантику оператора select, включая ключевое слово holdlock. Однако, в нем нельзя использовать конструкции compute, for browse и into.

Например, в следующем операторе объявляется курсор authors_crsr на результирующем множестве, которое содержит всех авторов, которые не живут в Калифорнии:

declare authors_crsr cursor
for select au_id, au_lname, au_fname
from authors
where state != 'CA'

Оператор выбора в объявлении курсора может содержать ссылки на названия параметров и локальные переменные. Однако эти параметры и локальные переменные должны быть определены в сохраненной процедуре, которая содержит оператор объявления курсора declare cursor. Если курсор используется в триггере, то в соответствующем операторе выбора можно ссылаться на временные триггерные таблицы inserted и deleted. Более подробную информацию об операторе выбора можно посмотреть в главе 2 «Запросы: выбор данных из таблицы».

Области действия курсора

Курсор определяется своей областью (диапазоном) действия, которая определяет временной промежуток (region), в течении которого курсор существует. Вне этого промежутка курсор перестает существовать. Области действия курсора определяются следующим образом:

Название курсора должно быть уникально в области его действия. В различных областях названия курсоров могут совпадать. Курсор определенный для одной области недоступен из других областей (диапазонов) действия. Однако, SQL Сервер позволяет использовать курсор в подобласти, если в ней не был определен курсор с тем же названием.

SQL Сервер определяет конфликты в названиях курсоров лишь в процессе выполнения. В сохраненной процедуре или триггере можно определить два курсора с одним названием, если в процессе исполнения они используются раздельно, как в следующем примере:

create procedure proc1 (@flag int)
as
if (@flag)
   declare names_crsr cursor
   for select au_fname from authors
else
   declare names_crsr cursor
   for select au_lname from authors
return

Эта процедура будет успешно выполнена, поскольку только один из курсоров names_crsr будет определен в процессе выполнения этой процедуры.

Развертывание курсора и результирующее множество

Результирующее множество курсора может не совпадать с данными из базовых таблиц. Например, курсор объявленный с конструкцией order by (упорядочить по) обычно требует создания внутренней таблицы для упорядочения строк результирующего множества. Кроме того, SQL Сервер не блокирует строки базовых таблиц, которые соответствуют строкам внутренней таблицы, что позволяет другим клиентам обновлять эти строки в базовых таблицах. В этом случае строки, которые видит клиент в результирующем множестве могут не отражать последних изменений, произошедших в базовых таблицах.

Результирующее множество курсора порождается по мере его продвижения. Это означает, что оператор выбора курсора выполняется как обычный запрос на выбор. Этот процесс известный как развертывание курсора (cursor scans) обеспечивает быстрое время ответа и не требует считывания строк, которые не нужны приложению в данный момент.

SQL Сервер требует, чтобы при развертывании курсора использовался уникальный индекс таблицы, особенно на нулевом уровне изоляции считывания (isolation level 0 reads). Если таблица содержит столбец-счетчик и необходимо создать неуникальный индекс для этой таблицы, то следует использовать опцию базы данных identity in nonunique index (счетчик в неуникальном индексе), что позволит автоматически включать столбец-счетчик в ключи табличных индексов и поэтому все они будут уникальными. Таким образом, эта опция делает логически неуникальные индексы внутренне уникальными, что позволяет использовать их в обновляемых курсорах на нулевом уровне изоляции считывания.

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

declare storinfo_crsr cursor
for select stor_id, stor_name, payterms
    from stores
    where state = 'CA'

Таблица stores, указанная в этом курсоре, вообще не содержит индексов. SQL Сервер допускает объявление курсора в таблице без уникальных индексов, но при обновлении или удалении из нее строк закрываются все курсоры в таких таблицах.

Создание обновляемых курсоров

Если курсор является обновляемым, то через него можно обновлять содержимое строк или удалять строки полностью. Если курсор предназначен только для чтения, то через него можно только считывать данные. По умолчанию SQL Сервер пытается сделать курсор обновляемым и если это не удается, то курсор предназначается для чтения.

Можно явно указать, является ли курсор обновляемым с помощью ключевых слов read only или update в операторе declare. Например, в следующем операторе определяется обновляемое результирующее множество для курсора pubs_crsr:

declare pubs_crsr cursor
for select pub_name, city, state
from publishers
for update of city, state

В этом примере результирующее множество будет включать все строки из таблицы publishers, но только поля city и state явно указаны как обновляемые.

Если через курсор не нужно обновлять или удалять, то его следует объявить только для чтения. Если явно не указано является ли курсор обновляемым или предназначенным только для чтения, то SQL Сервер по умолчанию считает курсор обновляемым, если соответствующий оператор выбора не содержит следующих конструкций:

Нельзя указывать предложение for update, если оператор выбора курсора содержит одну из вышеперечисленных конструкций. SQL Сервер устанавливает курсор только для чтения, если предложение order by содержится в операторе выбора этого курсора. Дополнительная информация о курсорах содержится в главе «Курсоры» Справочного руководства SQL Сервера.

Если в предложении for update не указывается список столбцов, то все столбцы будут обновляемыми. Как было отмечено ранее при описании развертывания курсора, SQL Сервер пытается использовать уникальные индексы для обновляемых курсоров, когда развертывает базовую таблицу. При наличии курсора SQL Сервер рассматривает индекс, содержащий столбец-счетчик, как уникальный, даже если он и не объявлен таковым.

SQL Сервер позволяет указывать в списке столбцов предложения for update названия столбцов, которых нет в операторе выбора курсора.

В следующем примере SQL Сервер использует уникальный индекс в столбце pub_id таблицы publishers (несмотря на то, что этого столбца нет в определении курсора newpubs_crsr):

declare newpubs_crsr cursor
for select pub_name, city, state
from publishers
for update

Если предложение for update не указано, то SQL Сервер может выбрать любой уникальный индекс или, при его отсутствии, любую комбинацию индексов для развертывания таблицы. Однако, если явно указано предложение for update, то должен существовать уникальный индекс, необходимый для развертывания базовой таблицы. В противном случае будет выдано сообщение об ошибке.

В списке столбцов предложения for update следует указывать столбцы, в которых необходимо обновлять данные, и в этом списке не должно быть столбцов, включенных в уникальные индексы. Это позволяет SQL Серверу использовать уникальные индексы для развертки таблицы и позволяет избежать аномального обновления известного как Проблема привидений (Halloween Problem).

Эта проблема возникает, когда клиент обновляет поле строки результирующего множества курсора, которое влияет на порядок расположения строк базовой таблицы. Например, если SQL Сервер получает доступ к базовой таблице используя индекс, и ключ (значение) индекса обновляется клиентом, то измененная строка может переместиться и следовательно может быть снова считана через курсор. Это результат того, что обновляющий курсор лишь логически создает результирующее множество. На самом деле это множество является подмножеством базовой таблицы, на основе которой получен курсор.

Открытие курсоров

После объявления курсора его необходимо открыть, чтобы получить доступ к отдельным строкам. Открытие курсора состоит из вычисления оператора выбора, указанного в определении курсора, и формирования его результирующего множества. Операция открытия имеет следующий вид:

open название_курсора

После открытия курсор располагается перед первой строкой результирующего множества. Теперь можно использовать операцию fetch (загрузка) для считывания первой строки результирующего множества.

SQL Сервер не позволяет открывать курсор, если он уже открыт или еще не объявлен. Можно снова открыть ранее закрытый курсор, чтобы вернуть курсор в начало результирующего множества.

Считывание строк данных с помощью курсоров

После объявления и открытия курсора можно выбирать строки из результирующего множества с помощью команды fetch (загрузить, сдвинуть). Эта команда возвращает клиенту одну или несколько строк. Можно включить в эту команду Transact-SQL параметры или локальные переменные для сохранения возвращаемых данных.

Синтаксис оператора fetch

Оператор fetch имеет следующий синтаксис:

fetch название_курсора [into список_переменных]

Например, после объявления и открытия курсора authors_crsr можно считать первую строку результирующего множества следующим образом:

fetch authors_crsr

au_id        au_lname           au_fname
-----------  -----------------  ------------
341-22-1782  Smith              Meander

Каждый последующий оператор fetch выбирает следующую строку результирующего множества. Например:

fetch authors_crsr

au_id         au_lname        au_fname
------------  --------------  ---------------
527-72-3246    Greene         Morningstar

После прохода всех строк курсор будет указывать на последнюю строку результирующего множества. Если вновь попытаться выполнить команду fetch, то SQL Сервер выдаст предупреждающее сообщение о состоянии переменной sqlstatus (описанной далее), которая указывает на отсутствие данных. Позиция курсора при этом не изменится.

Нельзя вновь прочитать строку, которая была уже пройдена, т.е. нельзя передвигаться по результирующему множеству в обратном направлении. Чтобы вернуться к началу, необходимо закрыть и затем вновь открыть результирующее множество, т.е. сгенерировать его снова.

В конструкции into указываются переменные, в которых SQL Сервер должен сохранить возвращаемые данные. Список_переменных должен состоять из ранее объявленных Transact-SQL параметров или локальных переменных.

Например, после объявления переменных @name, @city и @state можно сохранить в них поля строки, возвращаемой через курсор pubs_crsr:

fetch pubs_crsr into @name, @city, @state

SQL Сервер ожидает взаимно однозначного соответствия между переменными из списка и полями строки, возвращаемой через курсор. Типы переменных и параметров должны быть совместимы с типами данных столбцов результирующего множества.

Проверка состояния курсора

SQL Сервер возвращает информацию о состоянии (статусе) курсора после каждого чтения (загрузки). Информацию о состоянии можно также получить через глобальную переменную @@sqlstatus. В следующей таблице перечислены возможные значения этой переменной и их смысл:

Таблица 16-1: Значения переменной @sqlstatus

+---------------------------+-----------------------------------+
| Величина                  | Смысл                             |
+---------------------------+-----------------------------------+
| 0                         | Указывает на успешное окончание   |
|                           | оператора fetch.                  |
+---------------------------+-----------------------------------+
| 1                         | Указывает на ошибочное завершение |
|                           | оператора fetch.                  |
+---------------------------+-----------------------------------+
| 2                         | Указывает, что в результирующем   |
|                           | множестве больше нет данных для   |
|                           | чтения. Это предупреждение        |
|                           | выдается, если курсор находится   |
|                           | на последней строке и клиент      |
|                           | выдает команду fetch.             |
+---------------------------+-----------------------------------+

Следующий оператор определяет статус переменной @@sqlstatus для текущего открытого курсора authors_crsr:

select @@sqlstatus

-------------------
       0

(Выбрана 1 строка)

Только оператор fetch может устанавливать переменную @@sqlstatus. Другие операторы не затрагивают эту переменную.

Проверка количества загруженных строк

У SQL Сервера имеется также глобальная переменная @@rowcount. Она позволяет увидеть количество строк результирующего множества, возвращенных клиенту операторами fetch. Другими словами, в ней запоминается общее количество строк, просмотренных через курсор до текущего момента времени.

После чтения всех строк результирующего множества значение переменной @@rowcount совпадает с общим числом строк в этом множестве. Заметим, что на каждый открытый курсор заводится своя переменная @@rowcount. Эта переменная удаляется вместе с удалением курсора. Проверка значения переменной @@rowcount позволяет определить общее число строк, считанных через курсор операторами fetch.

В следующем примере определяется значение переменной @@rowcount для текущего открытого курсора authors_crsr:

select @@rowcount

-------------------
       1

(Выбрана 1 строка)

Получение нескольких строк одним оператором fetch

По умолчанию команда fetch позволяет получить одну строку данных за один раз. Пользователь может установить опцию cursor rows (курсорные строки), чтобы изменить число строк, возвращаемых одной командой fetch. Однако эта опция не влияет на операторы fetch, содержащие конструкцию into.

Команда установки этой опции имеет следующий вид:

set cursor rows число for название_курсора

где параметр число указывает на число возвращаемых через курсор строк. По умолчанию этот парметр равен 1 для каждого объявленного курсора. Установку этой опции можно сделать и при открытом и при закрытом курсоре.

Например, можно следующим образом изменить количество строк, возвращаемых через курсор authors_crsr:

set cursor rows 3 for authors_crsr

Теперь после каждого считывания оператор fetch будет возвращать три строки:

fetch authors_crsr

au_id        au_lname             au_fname
-----------  -------------------  ---------------
648-92-1872  Blotchet-Halls       Reginald
712-45-1867  del Castillo         Innes
722-51-5424  DeFrance             Michel

После считывания курсор будет расположен на последней переданной строке (в данном примере на авторе Michel DeFrance).

Передача нескольких строк за один раз особенно удобна для приложений клиента. Если пользователь считывает более одной строки за раз, то Открытый Клиент или Встроенный SQL (Open Client or Embedded SQL) автоматически буферизуют строки, переданные приложению клиента. Клиент по-прежнему имеет построчный доступ к данным, но при выполнении операторов fetch обращение к SQL Серверу происходит реже, что повышает производительность системы.

Обновление и удаление строк с помощью курсора

Если курсор является обновляемым, то через него можно обновлять и удалять строки. SQL Сервер анализирует оператор выбора, определяющий курсор, чтобы выяснить можно ли обновлять через этот курсор. Можно также явно указать на обновляющий курсор с помощью предложения for update в операторе объявления курсора declare cursor. Дополнительную информацию по этому поводу можно посмотреть в разделе "Создание обновляемых курсоров".

Удаление строк из результирующего множества курсора

С помощью конструкции where current of в операторе delete можно удалять строку, где находится курсор. Если строка удаляется из результирующего множества, то она также удаляется из соответствующей базовой таблицы. С помощью курсора за один раз можно удалить только одну строку.

Предложение delete... where current of имеет следующий синтаксис:

delete [from] [[база_данных.]владелец.]{название_таблицы | название_вьювера}
where current of название_курсора

Название таблицы или вьювера, указанные в этом предложении, должна совпадать с названием таблицы или вьювера, указанных в предложении from оператора выбора, определяющего курсор.

Например, можно удалить строку, на которую указывает курсор authors_crsr с помощью следующего оператора:

delete from authors
where current of authors_crsr

Ключевое слово from здесь можно не указывать.

Замечание:
Нельзя удалять строки с помощью курсора, который определен через соединение, даже если он объявлен как обновляемый.

После удаление строки с помощью курсора SQL Сервер располагает курсор перед строкой, которая следует за удаленной строкой в результирующем множестве. Нужно по-прежнему использовать оператор fetch, чтобы получить доступ к следующей строке. Если была удалена последняя строка, то SQL Сервер располагает курсор за последней строкой результирующего множества.

Например, после удаления строки в предыдущем примере (которая соответствует Мишелю ДеФрансу) можно просчитать следующие три строки результирующего множества следующим образом:

fetch authors_crsr

au_id        au_lname          au_fname
-----------  ----------------  ---------------
807-91-6654  Panteley          Sylvia
899-46-2035  Ringer            Anne
998-72-3567  Ringer            Albert

Конечно, можно удалить строку базовой таблицы и не обращаясь к курсору. Результирующее множество курсора будет изменяться в соответствии с изменением базовой таблицы.

Обновление строк результирующего множества курсора

Используя конструкцию where current of в операторе update, можно обновить содержимое строки, на которую указывает курсор. Каждое обновление результирующего множества курсора приводит к обновлению содержимого базовой таблицы, из которой получено множество курсора.

Оператор update... where current of имеет следующий синтаксис:

update [[база_данных.]владелец.]{название_таблицы | название_вьювера}
   set [[[база_данных.]владелец.]{название_таблицы | название_вьювера}]
       название_столбца1 = { выражение1 | NULL | (оператор_выбора)}
    [, название_столбца2 = { выражение2 | NULL | (оператор_выбора)}] ...
   where current of название_курсора

В предложении set указываются названия столбцов и их новые (обновляемые) значения. Если здесь указывается несколько столбцов, то они должны разделяться запятыми.

Название таблицы или вьювера, указанное в этом операторе, должно совпадать с названием таблицы или вьювера, указанным в предложении from оператора выбора, определяющего курсор. Если в предложении from указано несколько таблиц или вьюверов (в случае соединения), то можно указать только ту таблицу (вьювер), которая действительно обновляется.

Например, можно обновить строку, на которую указывает курсор pubs_crsr, следующим образом:

update publishers
set city = "Pasadena",
    state = "CA"
where current of pubs_crsr

После обновления позиция курсора остается неизменной. Можно продолжать обновление строки, на которую указывает курсор, до тех пор, пока другой SQL оператор не изменит позицию курсора.

SQL позволяет обновлять столбцы базовой таблицы, которые не были указаны в списке столбцов оператора выбора, определяющего курсор. Однако, если в предложении for update указывается список столбцов, то обновлять можно содержимое только этих столбцов.

Закрытие и удаление курсора

Когда работа с результирующем множеством закончена, курсор можно закрыть. Команда закрытия имеет следующий вид:

close название_курсора

Закрытие курсора не изменяет его определения. После этого можно вновь открыть курсор, тогда SQL Сервер создаст новое результирующее множество с помощью того же запроса. Например:

close authors_crsr
open authors_crsr

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

Например:

fetch authors_crsr

au_id        au_lname          au_fname
-----------  ----------------  ---------------
341-22-1782  Smith             Meander
527-72-3246  Greene            Morningstar
648-92-1872  Blotchet-Halls    Reginald

Если курсор больше не нужен, то его следует удалить (deallocate). Синтаксис оператора deallocate имеет следующий вид:

deallocate cursor название_курсора

Удаление курсора освобождает все ресурсы с ним связанные, включая название курсора. Нельзя вновь использовать название курсора до тех пор, пока курсор не удален. Если удаляется открытый курсор, SQL Сервер автоматически закрывает его. По окончанию соединения пользователя с сервером также закрываются и удаляются все курсоры.

Примеры использования курсора

Последующие примеры использования курсоров будут базироваться на следующем запросе:

select author = au_fname + " " + au_lname, au_id
from authors
order by au_lname

Результат этого запроса имеет следующий вид:

author                    au_id
------------------------  ----------------
Abraham Bennet            409-56-7008
Reginald Blotchet-Halls   648-92-1872
Cheryl Carson             238-95-7766
Michel DeFrance           722-51-5454
Ann Dull                  427-17-2319
Marjorie Green            213-46-8915
Morningstar Greene        527-72-3246
Burt Gringlesby           472-27-2349
Sheryl Hunter             846-92-7186
Livia Karsen              756-30-7391
Chastity Locksley         486-29-1786
Stearns MacFeather        724-80-9391
Heather McBadden          893-72-1158
Michael O'Leary          267-41-2394
Sylvia Panteley           807-91-6654
Anne Ringer               899-46-2035
Albert Ringer             998-72-3567
Meander Smith             341-22-1782
Dick Straight             274-80-9391
Dirk Stringer             724-08-9931
Johnson White             172-32-1176
Akiko Yokomoto            672-71-3249
Innes del Castillo        712-45-1867

В следующих пунктах показано как использовать курсор в этом запросе:

  1. Сначала необходимо объявить курсор.

    В операторе declare курсор определяется с помощью вышеприведенного оператора выбора:

    declare newauthors_crsr cursor for
    select author = au_fname + " " + au_lname, au_id
    from authors
    order by au_lname
    
  2. После объявления курсор можно открыть:

    open newauthors_crsr
    
  3. Теперь можно считывать строки, используя курсор:

    fetch newauthors_crsr
    
    author                 au_id
    --------------------   ---------------
    Abraham Bennet         409-56-7008
    
  4. Можно считывать несколько строк за один раз, установив число с помощью команды set:

    set cursor rows 5 for newauthors_crsr
    fetch newauthors_crsr
    
    author                      au_id
    -------------------------   ----------------
    Reginald Blotchet-Halls     648-92-1872
    Cheryl Carson               238-95-7766
    Michel DeFrance             722-51-5454
    Ann Dull                    427-17-2319
    Marjorie Green              213-46-8915
    

    Каждое последующее считывание будет сдвигать курсор еще на пять строк:

    fetch newauthors_crsr
    
    author                 au_id
    ---------------------  ------------
    Morningstar Greene     527-72-3246
    Burt Gringlesby        472-27-2349
    Sheryl Hunter          846-92-7186
    Livia Karsen           756-30-7391
    Chastity Locksley      486-29-1786
    
  5. После окончания работы с курсором его можно закрыть:

    close newauthors_crsr
    

    Закрытие курсора приводит к закрытию (releases) результирующего множества, но курсор остается определенным. Если его снова открыть командой open, то SQL Сервер снова выполняет запрос для формирования результирующего множества и устанавливает курсор перед первой строкой этого множества. По прежнему каждый оператор fetch будет считывать по пять строк.

    Для полного удаления курсора следует выполнить команду deallocate:

    deallocate cursor newauthors_crsr
    

Нельзя использовать название курсора до тех пор, пока курсор не удален командой deallocate:

Курсоры в сохраненных процедурах

Курсоры особенно полезны в сохраненных процедурах. С их помощью можно выполнить задание, требующее несколько запросов, всего одним запросом. Однако, все операции с курсором должны быть выполнены в одной процедуре. В сохраненной процедуре нельзя открывать, считывать или закрывать курсор, который не был объявлен в этой процедуре. Курсор не определен за пределами области действия (scope) сохраненной процедуры.

Например, следующая сохраненная процедура au_sales проверяет таблицу продаж, чтобы определить, продается ли у данного автора достаточно хорошо хотя бы одна книга:

create procedure au_sales (@author_id id)
as
/* declare local variables used for fetch */
declare @title_id tid
declare @title varchar(80)
declare @ytd_sales int
declare @msg varchar(120)

/* declare the cursor to get each book written by given author */
declare author_sales cursor for

select ta.title_id, t.title, t.total_sales
from titleauthor ta, titles t
where ta.title_id = t.title_id
  and ta.au_id = @author_id

open author_sales

fetch author_sales
       into @title_id, @title, @ytd_sales

if (@@sqlstatus = 2)
begin
   print "We do not sell books by this author."
   close author_sales
   return
end

/* if cursor result set is not empty, then process each row of
   information */
while (@@sqlstatus = 0)
begin
   if (@ytd_sales = NULL)
   begin
       select @msg = @title +
          " had no sales this year."
       print @msg
   end
   else if (@ytd_sales < 500)
   begin
       select @msg = @title +
           " had poor sales this year."
       print @msg
   end
   else if (@ytd_sales < 1000)
   begin
       select @msg = @title +
           " had mediocre sales this year."
       print @msg
   end
   else
   begin
       select @msg = @title +
           " had good sales this year."
       print @msg
   end
   fetch author_sales into @title_id, @title,
   @ytd_sales
end

/* if error occurred, call a designated handler */
if (@@sqlstatus = 1) exec error_handle
close author_sales
deallocate cursor author_sales
return

Дополнительную информацию о сохраненных процедурах можно получить в главе 14 "Использование сохраненных процедур".

Курсоры и блокировка

Методы блокировки при работе с курсором аналогичны обычным методам блокировки для SQL Сервера. Вообще говоря, операторы, считывающие данные (такие как select или readtext), используют разделяющую (shared) блокировку каждой страницы данных, чтобы предотвратить изменение данных со стороны неподтвержденных транзакций. Операторы обновления используют исключающую (exclusive) блокировку каждой страницы, которую они изменяют. Чтобы уменьшить вероятность тупиков (deadlocks) и улучшить производительность, SQL Сервер часто предваряет исключающую блокировку обновляющей блокировкой, которая указывает, что клиент собирается изменить данные на странице.

Для обновляющих курсоров SQL Сервер использует по умолчанию обновляющую блокировку при просмотре таблиц и вьюверов, указанных в предложении for update оператора declare cursor. Если предложение for update включено, но список таблиц пуст, то при обращении ко всем таблицам и вьюверам, указанным в предложении from оператора select, по умолчанию устанавливается обновляющая блокировка. Если предложение for update не указано, то при обращении ко всем таблицам и вьюверам устанавливается разделяющая блокировка. Чтобы использовалась разделяющая блокировка вместо обновляющей, необходимо добавить ключевое слово shared в предложении from. В частности, можно добавить слово shared к названию таблицы, для которой предпочтительна разделяющая блокировка.

Информация о блокировках SQL Сервера дается в Руководстве системного администратора SQL Сервера. Дополнительную информацию о курсорах и блокировках можно посмотреть в Справочном руководстве SQL Сервера.

Получение информации о курсорах

SQL Сервер предоставляет системную процедуру sp_cursorinfo, которая дает информацию о названии курсора, его текущем состоянии (таком как открыт или закрыт) и столбцах результирующего множества. В следующем примере дается информация о курсоре authors_crsr:

sp_cursorinfo 0, authors_crsr

Cursor name 'authors_crsr' is declared at nesting level '0'.
The cursor id is 327681
The cursor has been successfully opened 1 times
The cursor was compiled at isolation level 1.
The cursor is not open.
The cursor will remain open when a transaction is committed or rolled back.
The number of rows returned for each FETCH is 1.
The cursor is updatable.

There are 3 columns returned by this cursor.
The result columns are:

Name = 'au_id', Table = 'authors', Type = ID, Length = 11 (updatable)
Name = 'au_lname', Table = 'authors', Type = VARCHAR, Length = 40 (updatable)
Name = 'au_fname', Table = 'authors', Type = VARCHAR, Length = 20 (updatable)
Previous page:
Триггера: поддержка ссылочной целостности
Top:
DRKB
Next page:
Транзакции