Интернет Windows Android

Курсоры. Команда DECLARE CURSOR общие правила Курсоры transact sql

In my T-SQL code I always use set based operations. I have been told these types of operations are what SQL Server is designed to process and it should be quicker than serial processing. I know cursors exist but I am not sure how to use them. Can you provide some cursor examples? Can you give any guidance on when to use cursors? I assume Microsoft included them in SQL Server for a reason so they must have a place where they can be used in an efficient manner.

Solution

In some circle"s cursors are never used, in others they are a last resort and in other groups they are used regularly. In each of these camps they have different reasons for their stand on cursor usage. Regardless of your stand on cursors they probably have a place in particular circumstances and not in others. So, it boils down to your understanding of the coding technique then your understanding of the problem at hand to make a decision on whether or not cursor-based processing is appropriate or not. To get started let"s do the following:

  • Look at an example cursor
  • Break down the components of the cursor
  • Provide additional cursor examples
  • Analyze the pros and cons of cursor usage

How to Create a SQL Server Cursor

Creating a SQL Server cursor is a consistent process, so once you learn the steps you are easily able to duplicate them with various sets of logic to loop through data. Let"s walk through the steps:

  1. First, you declare your variables that you need in the logic.
  2. Second you declare cursor with a specific name that you will use throughout the logic. This is immediately followed by opening the cursor.
  3. Third, you fetch a record from cursor to begin the data processing.
  4. Fourth, is the data process that is unique to each set of logic. This could be inserting, updating, deleting, etc. for each row of data that was fetched. This is the most important set of logic during this process that is performed on each row.
  5. Fifth, you fetch the next record from cursor as you did in step 3 and then step 4 is repeated again by processing the selected data.
  6. Sixth, once all of the data has been processed, then you close cursor.
  7. As a final and important step, you need to deallocate the cursor to release all of the internal resources SQL Server is holding.

From here, check out the examples below to get started on knowing when to use SQL Server cursors and how to do so.

Example SQL Server Cursor

Here is an example cursor from tip Simple script to backup all SQL Server databases where backups are issued in a serial manner:

DECLARE @name VARCHAR(50) -- database name DECLARE @path VARCHAR(256) -- path for backup files DECLARE @fileName VARCHAR(256) -- filename for backup DECLARE @fileDate VARCHAR(20) -- used for file name SET @path = "C:\Backup\" SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112) DECLARE db_cursor CURSOR FOR SELECT name FROM MASTER.dbo.sysdatabases WHERE name NOT IN ("master","model","msdb","tempdb") OPEN db_cursor FETCH NEXT FROM db_cursor INTO @name WHILE @@FETCH_STATUS = 0 BEGIN SET @fileName = @path + @name + "_" + @fileDate + ".BAK" BACKUP DATABASE @name TO DISK = @fileName FETCH NEXT FROM db_cursor INTO @name END CLOSE db_cursor DEALLOCATE db_cursor

SQL Server Cursor Components

Based on the example above, cursors include these components:

  • DECLARE statements - Declare variables used in the code block
  • SET\SELECT statements - Initialize the variables to a specific value
  • DECLARE CURSOR statement - Populate the cursor with values that will be evaluated
    • NOTE - There are an equal number of variables in the DECLARE CURSOR FOR statement as there are in the SELECT statement. This could be 1 or many variables and associated columns.
  • OPEN statement - Open the cursor to begin data processing
  • FETCH NEXT statements - Assign the specific values from the cursor to the variables
    • NOTE - This logic is used for the initial population before the WHILE statement and then again during each loop in the process as a portion of the WHILE statement
  • WHILE statement - Condition to begin and continue data processing
  • BEGIN...END statement - Start and end of the code block
    • NOTE - Based on the data processing multiple BEGIN...END statements can be used
  • Data processing - In this example, this logic is to backup a database to a specific path and file name, but this could be just about any DML or administrative logic
  • CLOSE statement - Releases the current data and associated locks, but permits the cursor to be re-opened
  • DEALLOCATE statement - Destroys the cursor

Recommended Reading

Learn more about SQL Server Cursors and alternatives:

Additional SQL Server Cursor Examples

In the example above backups are issued via a cursor, check out these other tips that leverage cursor-based logic:

  • Script to create commands to disable, enable, drop and recreate Foreign Key constraints in SQL Server

SQL Server Cursor Analysis

The analysis below is intended to serve as insight into various scenarios where cursor-based logic may or may not be beneficial:

  • Online Transaction Processing (OLTP) - In most OLTP environments, SET based logic makes the most sense for short transactions. Our team has run into a third-party application that uses cursors for all of its processing, which has caused issues, but this has been a rare occurrence. Typically, SET based logic is more than feasible and cursors are rarely needed.
  • Reporting - Based on the design of the reports and the underlying design, cursors are typically not needed. However, our team has run into reporting requirements where referential integrity does not exist on the underlying database and it is necessary to use a cursor to correctly calculate the reporting values. We have had the same experience when needing to aggregate data for downstream processes, a cursor-based approach was quick to develop and performed in an acceptable manner to meet the need.
  • Serialized processing - If you have a need to complete a process in serialized manner, cursors are a viable option.
  • Administrative tasks - Many administrative tasks need to be executed in a serial manner, which fits nicely into cursor-based logic, but other system-based objects exist to fulfill the need. In some of those circumstances, cursors are used to complete the process.
  • Large data sets - With large data sets you could run into any one or more of the following:
    • Cursor based logic may not scale to meet the processing needs.
    • With large set-based operations on servers with a minimal amount of memory, the data may be paged or monopolize the SQL Server which is time consuming can cause contention and memory issues. As such, a cursor-based approach may meet the need.
    • Some tools inherently cache the data to a file under the covers, so processing the data in memory may or may not actually be the case.
    • If the data can be processed in a staging SQL Server database the impacts to the production environment are only when the final data is processed. All of the resources on the staging server can be used for the ETL processes then the final data can be imported.
    • SSIS supports batching sets of data which may resolve the overall need to break-up a large data set into more manageable sizes and perform better than a row by row approach with a cursor.
    • Depending on how the cursor or SSIS logic is coded, it may be possible to restart at the point of failure based on a
    • Repeat a batch with the GO command
    Next Steps
    • When you are faced with a data processing decision determine where you stand with SQL Server cursor usage. They may or may not have a place in your application or operational processes. There are many ways to complete a task, so using a cursor could be a reasonable alternative or not. You be the judge.
    • If you run into issues with another coding technique and need to get something done quickly, using a cursor may be a viable alternative. It may take longer to process the data, but the coding time might be much less. If you have a one-time process or nightly processing, this could do the trick.
    • If cursors are shunned in your environment, be sure to select another viable alternative. Just be sure the process will not cause other issues. As an example, if a cursor is used and millions of rows are processed will this potentially flush all of the data from cache and cause further contention? Or with a large data set will the data be paged to disk or written to a temporary directory?
    • As you evaluate a cursor-based approach versus other alternatives make a fair comparison of the techniques in terms of time, contention and resources needed. Hopefully these factors will drive you to the proper technique.

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

Курсоры позволяют работать со строками таблицы посредством указания их порядкового номера в наборе данных;

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

Жизненный цикл курсора:

Создание курсора: DECLARE <имя курсора> [ INSENSITIVE ] [ SCROLL ] CURSOR FOR < SELECT -оператор> FOR { READ ONLY | UPDATE }

Здесь ключевое слово INSENSITIVE означает, что курсор будет статическим (слепок с данных), в то время как по умолчанию курсор создаётся динамическим (выборка осуществляется каждый раз при обращении к строке). Ключевое слово SCROLL означает, что курсор можно прокручивать в любом направлении, иначе курсор создаётся «последовательным».

Открытие курсора: OPEN [ GLOBAL ] <имя курсора>. Курсор, указанный как GLOBAL , не удаляется автома-тически при завершении работы той процедуры или пакета, из которых он был вызван.

Считывание данных : FETCH [[ NEXT | PRIOR | FIRST | LAST | ABSOLUTE n | RELATIVE n ] FROM ] [ GLOBAL ] <имя курсора> [ INTO @ variable _ name , …]. SQL Server 2000 позволяет считывать из курсора всего одну строку. Ключевое слово FIRST – возвратить первую строку курсора; LAST – последнюю строку курсора; NEXT – следующую строку за текущей, возвращённая строка становится текущей; PRIOR – предыдущую перед текущей; ABSOLUTE n – возвращает строку по её абсолютному порядковому номеру в курсоре; RELATIVE – через n строк после текущей. Данные столбцов будут сохраняться в каждую из указанных переменных в порядке их перечисления.

Изменение данных: выполняет команда UPDATE с синтаксисом, предназначенным для работы с курсорами.

Удаление данных: выполняет команда DELETE с синтаксисом, предназначенным для работы с курсорами.

Закрытие курсора: CLOSE [ GLOBAL ] <имя курсора>

Освобождение курсора: DEALLOCATE [ GLOBAL ] <имя курсора>

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

DECLARE fo_curs CURSOR STATIC FOR

SELECT name_rus from fo ORDER BY name_rus

DECLARE @name varchar(50)

FETCH FIRST FROM fo_curs INTO @name

WHILE @@FETCH_STATUS=0

FETCH NEXT FROM fo_curs INTO @name

DEALLOCATE fo_curs

2.7. Обеспечение безопасности и сохранности данных в Microsoft SQL Server. Управление базами данных. Роли. Назначение прав пользователям (GRANT, DENY, REVOKE). Методы и технологии защиты данных в SQL Server.

Система безопасности и администрирование SQL Server . .

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

Управление базами данных

Для создания базы данных средствами TSQL используется команда CREATE DATABASE, однако обычно для этой цели используются возможности SQL Server Management Studio. В SQL сервер определено достаточно много операций с базами данных: увеличение (уменьшение) размеров файлов, изменение конфигурации (команда ALTER), присоединение и отсоединение, передача прав владения, изменение имени, просмотр свойств и, наконец, удаление (DROP DATABASE).

Как и в большинстве серверов баз данных, в SQL Server существует пользователь, наделенный всеми административными полномочиями - это System Administrator или ‘sa" . После начальной установки сервера пароль sa пуст. Пользователь, создающий новую базу данных, автоматически становится её владельцем (‘dbo" – Data Base Owner). В момент создания базы определяется и пользователь "guest". Если учётная запись пользователя явно не отображается в пользователя конкретной базы данных, пользователю предоставляется неявный доступ с использованием гостевого имени guest. Обычно guest запрещают.

Пользователь, создавший объект в базе данных, автоматически становится его владельцем, и никто, включая dbo и sa , не могут использовать этот объект, пока владелец не назначит им права на него. Но чтобы пользователь мог создать объект, владелец БД должен сначала ему предоставить соответствующие права.

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

Db_owner. Имеет все права в базе данных

Db_accessadmin. Может добавлять или удалять пользователей

Db_securityadmin. Управляет всеми разрешениями, объектами, ролями и пользователями

Db_ddladmin. Может выполнять все команды DDL, кроме GRANT, DENY, REVOKE

Db_backupoperator. Может выполнять команды архивир. данных

Db_datareader. Может просматр. любые данные в любой таблице

Db_datawriter. Может модифиц. любые данные в любой таблице

Db_denydatareader. Запрещ. просматр. люб. данные в люб. таблиц

Db_denydatawriter. Запрещ модифицир люб данные в люб таблиц

Назначение прав пользователям. Основой системы безопасности SQL Server являются (1) учётные записи (accounts); (2) пользователи (users); (3) роли (roles); (4) группы (groups).

Когда пользователь подключается к SQL Server , действия, которые он может выполнять, определяются правами, выданными ему как пользователю и члену роли. Права выдаются администратором СУБД, владельцем базы данных или владельцем конкретного объекта БД. Права в БД можно разделить на три категории: (1) права на доступ к объектам баз данных; (2) права на выполнение команд TSQL ; (3) неявные права. Сервер позволяет передавать права владения от одного пользователя другому.

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

GRANT { ALL | < вид действия >,…}

{ ON {<имя таблицы или представления>} [(<имя столбца>,…)]

| ON {< имя хранимой процедуры >}

| ON {< имя пользовательской функции >}

TO { PUBLIC | <имя объекта системы безопасности>,…}

[ AS <имя группы> | <имя роли>]

назначение прав пользователям , где

ALL – пользователю предоставляются все возможные разрешения, иначе указать

<вид действия> – права на доступные для пользователя действия, а именно:

SELECT – на просмотр, для столбца таблицы и для таблицы (представления)

INSERT – на добавление, для таблицы (представления) в целом

UPDATE – на изменение, для столбца таблицы и для таблицы (представления)

DELETE – на удаление, для таблицы (представления) в целом

EXECUTE – на выполнение хранимых процедур

REFERENCES – возможность ссылаться на указанный объект (вводить в состав внешнего ключа).

<имя объекта системы безопасности> – учётные записи SQL Server , пользователи домена Windows; PUBLIC – для всех пользователей.

WITH GRANT OPTION – позволяет пользователю, которому сейчас предоставляются права, самому назначать права на доступ к объекту другим пользователям.

AS <имя группы> | <имя роли> – участие пользователя в роли, которой предоставлена возможность предоставлять права другим пользователям.

GRANT SELECT ON authors TO public

GRANT INSERT, UPDATE, DELETE ON authors TO Mary, John, Tom

GRANT SELECT ON Plan_Data TO Accounting WITH GRANT OPTION

GRANT SELECT ON Plan_Data TO Jack AS Accounting

Джек не входит в роль Accounting , но кто-нибудь из этой роли может предоставить право

DENY { ALL | < вид действия >,…}

{ ON {<имя таблицы или представления>} [(<имя столбца>,…)]

| ON {<имя хранимой процедуры>}

| ON {<имя пользовательской функции>}

TO { PUBLIC | <имя объекта системы безопасности>,…}

запрещение доступа пользователям к объектам базы данных. CASCADE отзывает права не только у данного пользователя, но также и у всех, кому он права предоставлял.

Пример (на запрещение выполнения команды TSQL):

DENY CREATE TABLE TO Jack CASCADE

Команда REVOKE используется для неявного отклонения доступ к объектам базы данных. Синтаксис аналогичен команде DENY. Неявное отклонение подобно запрещению доступа с тем отличием, что оно действует только на том уровне, на котором определено. Пример: пользователю Jack , который является участником роли GoodUsers , предоставлены права на доступ к таблице XFiles . Если при помощи REVOKE для роли GoodUsers отклоняются права на доступ к этой таблице, пользователь Jack всё равно может обращаться к этой таблице, поскольку права для него определены явно. Если же применить REVOKE персонально для него, он потеряет право на доступ к XFiles.

Разрешения, предоставленные роли, наследуются их членами. Если пользователю предоставлен доступ к объекту через членство в одной роли, но запрещён в другой, то конфликт доступа всегда решается в пользу запрещения.

Технологии защиты данных в MS SQL Server

1.Механизм checkpoints – контрольных точек, которые генерируются через ~60 с для записи обновлённых страниц на диск (контрольная точка может быть поставлена принудительно командой CHECKPOINT).

2.Встроенные и внешние механизмы проверки целостности базы данных (запускаются автоматически или, как утилита DBCC – Database Consistency Checker – вручную).

3.Физическое дублирование (если оно разрешено) файлов баз данных средствами операционной системы (включая механизм зеркальных жёстких дисков).

4.Резервирование баз данных и журналов транзакций – путём записи дампа базы данных на устройство резервирования (магнитную ленту или жёсткий диск).

5.Репликация – возможность дублирования информации путём её периодической (в некоторых случаях – синхронной) передачи с одного SQL сервера на другой.

6.Шифрование трафика между клиентом и сервером, а также шифрование кодов, использованных для работы с объектами БД (хранимых процедур, триггеров и др.)

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

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

Запрос к реляционной базе данных обычно возвращает несколько рядов (записей) данных, но приложение за один раз обрабатывает лишь одну запись. Даже если оно имеет дело одновременно с несколькими рядами (например, выводит данные в форме электронных таблиц), их количество по-прежнему ограничено. Кроме того, при модификации, удалении или добавлении данных рабочей единицей является ряд. В этой ситуации на первый план выступает концепция курсора , и в таком контексте курсор – указатель на ряд.

Курсор в SQL – это область в памяти базы данных, которая предназначена для хранения последнего оператора SQL. Если текущий оператор – запрос к базе данных, в памяти сохраняется и строка данных запроса, называемая текущим значением, или текущей строкой курсора . Указанная область в памяти поименована и доступна для прикладных программ.

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

В соответствии со стандартом SQL при работе с курсорами можно выделить следующие основные действия :

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

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

В некоторых случаях применение курсора неизбежно. Однако по возможности этого следует избегать и работать со стандартными командами обработки данных: SELECT , UPDATE , INSERT , DELETE . Помимо того, что курсоры не позволяют проводить операции изменения над всем объемом данных, скорость выполнения операций обработки данных посредством курсора заметно ниже, чем у стандартных средств SQL.

Реализация курсоров в среде MS SQL Server

SQL Server поддерживает три вида курсоров :

  • курсоры SQL применяются в основном внутри триггеров, хранимых процедур и сценариев;
  • курсоры сервера действуют на сервере и реализуют программный интерфейс приложений для ODBC, OLE DB, DB_Library;
  • курсоры клиента реализуются на самом клиенте. Они выбирают весь результирующий набор строк из сервера и сохраняют его локально, что позволяет ускорить операции обработки данных за счет снижения потерь времени на выполнение сетевых операций.

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

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

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

SQL Server поддерживает курсоры статические , динамические , последовательные и управляемые набором ключей.

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

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

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

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

Курсор, управляемый набором ключей , находится посередине между этими крайностями. Записи идентифицируются на момент выборки, и тем самым отслеживаются изменения . Такой тип курсора полезен при реализации прокрутки назад – тогда добавления и удаления рядов не видны, пока информация не обновится, а драйвер выбирает новую версию записи, если в нее были внесены изменения .

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

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

Управление курсором в среде MS SQL Server

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

  • DECLARE – создание или объявление курсора ;
  • OPEN – открытие курсора , т.е. наполнение его данными;
  • FETCH – выборка из курсора и изменение строк данных с помощью курсора;
  • CLOSE – закрытие курсора ;
  • DEALLOCATE – освобождение курсора , т.е. удаление курсора как объекта.

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

В стандарте SQL для создания курсора предусмотрена следующая команда:

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

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

SELECT-оператор задает тело запроса SELECT , с помощью которого определяется результирующий набор строк курсора .

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

Создание курсора с аргументом FOR UPDATE позволяет выполнять в курсоре изменение данных либо в указанных столбцах, либо, при отсутствии аргумента OF имя_столбца , во всех столбцах.

В среде MS SQL Server принят следующий синтаксис команды создания курсора :

<создание_курсора>::= DECLARE имя_курсора CURSOR FOR SELECT_оператор ]]

При использовании ключевого слова LOCAL будет создан локальный курсор , который виден только в пределах создавшего его пакета, триггера, хранимой процедуры или пользовательской функции. По завершении работы пакета, триггера, процедуры или функции курсор неявно уничтожается. Чтобы передать содержимое курсора за пределы создавшей его конструкции, необходимо присвоить его параметру аргумент OUTPUT .

Если указано ключевое слово GLOBAL , создается глобальный курсор ; он существует до закрытия текущего соединения.

При указании FORWARD_ONLY создается последовательный курсор ; выборку данных можно осуществлять только в направлении от первой строки к последней.

При указании SCROLL создается прокручиваемый курсор ; обращаться к данным можно в любом порядке и в любом направлении.

При указании STATIC создается статический курсор .

При указании KEYSET создается ключевой курсор.

При указании DYNAMIC создается динамический курсор .

Если для курсора READ_ONLY указать аргумент FAST_FORWARD , то созданный курсор будет оптимизирован для быстрого доступа к данным. Этот аргумент не может быть использован совместно с аргументами FORWARD_ONLY и OPTIMISTIC .

В курсоре , созданном с указанием аргумента OPTIMISTIC , запрещается изменение и удаление строк , которые были изменены после открытия курсора .

При указании аргумента TYPE_WARNING сервер будет информировать пользователя о неявном изменении типа курсора , если он несовместим с запросом SELECT .

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

Для открытия курсора и наполнения его данными из указанного при создании курсора запроса SELECT используется следующая команда:

После открытия курсора происходит выполнение связанного с ним оператора SELECT , выходные данные которого сохраняются в многоуровневой памяти.

Выборка данных из курсора

Сразу после открытия курсора можно выбрать его содержимое (результат выполнения соответствующего запроса) посредством следующей команды:

При указании FIRST будет возвращена самая первая строка полного результирующего набора курсора , которая становится текущей строкой.

При указании LAST возвращается самая последняя строка курсора . Она же становится текущей строкой.

При указании NEXT возвращается строка, находящаяся в полном результирующем наборе сразу же после текущей. Теперь она становится текущей. По умолчанию команда FETCH использует именно этот способ выборки строк.

Ключевое слово PRIOR возвращает строку, находящуюся перед текущей. Она и становится текущей.

Аргумент ABSOLUTE {номер_строки | @переменная_номера_строки} возвращает строку по ее абсолютному порядковому номеру в полном результирующем наборе курсора . Номер строки можно задать с помощью константы или как имя переменной, в которой хранится номер строки. Переменная должна иметь целочисленный тип данных. Указываются как положительные, так и отрицательные значения. При указании положительного значения строка отсчитывается от начала набора, отрицательного – от конца. Выбранная строка становится текущей. Если указано нулевое значение, строка не возвращается.

Аргумент RELATIVE {кол_строки | @переменная_кол_строки} возвращает строку, находящуюся через указанное количество строк после текущей. Если указать отрицательное значение числа строк, то будет возвращена строка, находящаяся за указанное количество строк перед текущей. При указании нулевого значения возвратится текущая строка. Возвращенная строка становится текущей.

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

В конструкции INTO @имя_переменной [,...n] задается список переменных, в которых будут сохранены соответствующие значения столбцов возвращаемой строки. Порядок указания переменных должен соответствовать порядку столбцов в курсоре , а тип данных переменной – типу данных в столбце курсора . Если конструкция INTO не указана, то поведение команды FETCH будет напоминать поведение команды SELECT – данные выводятся на экран.

Изменение и удаление данных

Для выполнения изменений с помощью курсора необходимо выполнить команду UPDATE в следующем формате:

За одну операцию могут быть изменены несколько столбцов текущей строки курсора , но все они должны принадлежать одной таблице.

Для удаления данных посредством курсора используется команда DELETE в следующем формате:

В результате будет удалена строка, установленная текущей в курсоре .

Закрытие курсора

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

Освобождение курсора

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

Для контроля достижения конца курсора рекомендуется применять функцию: @@FETCH_STATUS

Функция @@FETCH_STATUS возвращает:

0 , если выборка завершилась успешно;

1 , если выборка завершилась неудачно вследствие попытки выборки строки, находящейся за пределами курсора ;

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

DECLARE @id_kl INT, @firm VARCHAR(50), @fam VARCHAR(50), @message VARCHAR(80), @nam VARCHAR(50), @d DATETIME, @p INT, @s INT SET @s=0 PRINT " Список покупок" DECLARE klient_cursor CURSOR LOCAL FOR SELECT КодКлиента, Фирма, Фамилия FROM Клиент WHERE Город="Москва" ORDER BY Фирма, Фамилия OPEN klient_cursor FETCH NEXT FROM klient_cursor INTO @id_kl, @firm, @fam WHILE @@FETCH_STATUS=0 BEGIN SELECT @message="Клиент "+@fam+ " Фирма "+ @firm PRINT @message SELECT @message="Наименование товара Дата покупки Стоимость" PRINT @message DECLARE tovar_cursor CURSOR FOR SELECT Товар.Название, Сделка.Дата, Товар.Цена*Сделка.Количество AS Стоимость FROM Товар INNER JOIN Сделка ON Товар. КодТовара=Сделка.КодТовара WHERE Сделка.КодКлиента=@id_kl OPEN tovar_cursor FETCH NEXT FROM tovar_cursor INTO @nam, @d, @p IF @@FETCH_STATUS<>0 PRINT " Нет покупок" WHILE @@FETCH_STATUS=0 BEGIN SELECT @message=" "+@nam+" "+ CAST(@d AS CHAR(12))+" "+ CAST(@p AS CHAR(6)) PRINT @message SET @s=@s+@p FETCH NEXT FROM tovar_cursor INTO @nam, @d, @p END CLOSE tovar_cursor DEALLOCATE tovar_cursor SELECT @message="Общая стоимость "+ CAST(@s AS CHAR(6)) PRINT @message -- переход к следующему клиенту-- FETCH NEXT FROM klient_cursor INTO @id_kl, @firm, @fam END CLOSE klient_cursor DEALLOCATE klient_cursor Пример 13.6. Курсор для вывода списка приобретенных клиентами из Москвы товаров и их общей стоимости.

Пример 13.7. Разработать прокручиваемый курсор для клиентов из Москвы. Если номер телефона начинается на 1, удалить клиента с таким номером и в первой записи курсора заменить первую цифру в номере телефона на 4.

DECLARE @firm VARCHAR(50), @fam VARCHAR(50), @tel VARCHAR(8), @message VARCHAR(80) PRINT " Список клиентов" DECLARE klient_cursor CURSOR GLOBAL SCROLL KEYSET FOR SELECT Фирма, Фамилия, Телефон FROM Клиент WHERE Город="Москва" ORDER BY Фирма, Фамилия FOR UPDATE OPEN klient_cursor FETCH NEXT FROM klient_cursor INTO @firm, @fam, @tel WHILE @@FETCH_STATUS=0 BEGIN SELECT @message="Клиент "+@fam+ " Фирма "+@firm " Телефон "+ @tel PRINT @message -- если номер телефона начинается на 1, -- удалить клиента с таким номером IF @tel LIKE ‘1%’ DELETE Клиент WHERE CURRENT OF klient_cursor ELSE -- переход к следующему клиенту FETCH NEXT FROM klient_cursor INTO @firm, @fam, @tel END FETCH ABSOLUTE 1 FROM klient_cursor INTO @firm, @fam, @tel -- в первой записи заменить первую цифру в -- номере телефона на 4 UPDATE Клиент SET Телефон=’4’ + RIGHT(@tel,LEN(@tel)-1)) WHERE CURRENT OF klient_cursor SELECT @message="Клиент "+@fam+" Фирма "+ @firm " Телефон "+ @tel PRINT @message CLOSE klient_cursor DEALLOCATE klient_cursor Пример 13.7. Прокручиваемый курсор для клиентов из Москвы.

Пример 13.8. Использование курсора как выходного параметра процедуры . Процедура возвращает набор данных – список товаров.

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

DECLARE @my_cur CURSOR DECLARE @n VARCHAR(20) EXEC my_proc @cur=@my_cur OUTPUT FETCH NEXT FROM @my_cur INTO @n SELECT @n WHILE (@@FETCH_STATUS=0) BEGIN FETCH NEXT FROM @my_cur INTO @n SELECT @n END CLOSE @my_cur DEALLOCATE @my_cur

Ребека М. Риордан "Курсоры в Transact-SQL"

Интернет-Университет Информационных Технологий

http://www.INTUIT.ru

Учебный курс: «Программирование в Microsoft SQL Server 2000»

Курсор – это особый временный объект SQL, предназначенный для использования в программах и хранимых процедурах. С его помощью можно в цикле пройти по результирующему набору строк запроса, по отдельности считывая и обрабатывая каждую его строку. В хранимых процедурах с помощью курсоров можно выполнять сложные вычисления, которые трудно выразить с помощью синтаксиса инструкции SELECT. Большой теоретический материал урока дополнен очень хорошими примерами. В частности, рассматривается применение функции CURSOR_STATUS, описание переменных @@CURSOR_ROWS и @@FETCH_STATUS, и многое другое.

Вы научитесь:

  • объявлять курсор;
  • открывать курсор;
  • закрывать курсор;
  • освобождать курсор;
  • использовать простую команду FETCH;
  • осуществлять выборку строки в переменные;
  • осуществлять выборку строки по ее абсолютной позиции;
  • осуществлять выборку строки по ее относительной позиции;
  • выполнять позиционную модификацию;
  • выполнять позиционное удаление;
  • использовать глобальную переменную @@CURSOR_ROWS для определения количества строк во множестве курсора;
  • использовать глобальную переменную @@FETCH_STATUS для определения результатов выполнения команды FETCH;
  • использовать функцию CURSOR_STATUS для запроса статуса курсора.

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

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

Чтобы справиться с подобными ситуациями, в SQL предусмотрены курсоры. Курсор представляет собой объект, который указывает на определенную строку во множестве. В зависимости от сути созданного вами курсора, вы можете перемещать курсор внутри множества и модифицировать или удалять данные.

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

Microsoft SQL Server реально поддерживает два различных типа курсоров: курсоры Transact-SQL и курсоры API (курсоры программного интерфейса приложений). Курсоры API создаются внутри приложения, использующего объекты Microsoft ActiveX Data Objects (ADO), OLE DB, ODBC или DB-Library. Каждое из этих API поддерживает несколько отличающиеся функциональные возможности и использует различный синтаксис. Здесь мы не будем подробно обсуждать курсоры API; если вы планируете использовать их, обратитесь к соответствующей документации на API и языку программирования, который вы собираетесь применить.

Курсоры Transact-SQL создаются с помощью команды DECLARE CURSOR. Как объект курсора, так и множество строк, на которое он указывает, должны существовать на сервере. Подобные курсоры называются серверными курсорами. Если вы используете серверный курсор из приложения, соединенного с SQL Server через сеть, каждая операция с курсором требует двустороннего сетевого взаимодействия. Библиотеки API-курсоров, поддерживающие серверные курсоры, поддерживают также клиентский курсор, который существует в клиентской системе и кэширует строки, которые он обрабатывает на клиенте.

Множество строк, на которое указывает курсор, определяется с помощью команды SELECT. При создании курсора Transact-SQL на команду SELECT накладываются несколько ограничений:

команда SELECT не может возвращать несколько результирующих множеств;

команда SELECT не может содержать фразу INTO для создания новой таблицы;

команда SELECT не может содержать фразу COMPUTE или COMPUTE BY, используемые для агрегирования результатов. (Однако, она может содержать функции агрегирования, например, AVG.)

Характеристики курсоров

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

Отражение изменений

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

SELECT * FROM Oils WHERE Left(OilName, 1) = "B" База данных Aromatherapy вернет четыре строки, как показано на рис.1. Если в процессе использования вами курсора кто-либо добавит значение Description для элемента Bergamot, либо добавит строку для элемента Bayberry, что произойдет с множеством строк, на которое указывает ваш курсор?

Рис. 1. База данных Aromatherapy содержит четыре строки, начинающиеся с буквы В.

При создании вашего курсора могут быть независимо определены два вида чувствительности: изменения каких строк включаются во множество (членство множества) и отражение изменений в исходных строках.

Прокрутка

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

Обновление

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

Типы курсоров

Transact-SQL поддерживает четыре различных типа курсоров: статические, ключевые, динамические и курсоры быстрого доступа, или "пожарные" (firehose). Каждый тип курсора хранит различные данные относительно строк, на которые он указывает, кроме того, каждому типу курсора свойственны различные сочетания характеристик, рассмотренных в предыдущем разделе.

Статические курсоры

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

Ключевые курсоры

Ключевой курсор копирует в базу tempdb только те столбцы, которые уникально идентифицируют каждую строку. Чтобы иметь возможность объявить ключевой курсор, каждая таблица, входящая в определение оператора SELECT, должна иметь уникальный индекс, который задает копируемый набор – ключ.

Ключевые курсоры могут быть как модифицируемыми, так и иметь режим "только для чтения". Они также могут быть прокручиваемыми или последовательными.

Членство в ключевом курсоре фиксируется на момент объявления курсора. Если в процессе открытого состояния курсора добавляется строка, удовлетворяющая условию отбора, она не будет добавлена во множество. В нашем предыдущем примере, где в качестве условия отбора использовалось LEFT(OilName, 1) = "B", новая строка со значением поля OilName "Bayberry" не будет добавлена к строкам, относящимся к области действия курсора.

Аналогично, если изменение вносится в строку, которая после этого не будет удовлетворять условию членства во множестве, например, замена "Basil" на "Kumquat", строка все же останется членом множества. Даже если строка удаляется, она по-прежнему остается членом множества, но SQL Server возвращает NULL для всех значений столбцов.

Хотя членство во множестве курсора после открытия курсора остается фиксированным, тем не менее, изменения значений данных, вносимые в исходные таблицы, находят отражение. Например, изменение значения поля Description для строки Bergamot будет возвращено курсором. Однако изменения значений множества ключей отражаются в курсорах только в том случае, если они осуществляются внутри курсора. В продолжение предыдущего примера, если значение поля OilName было изменено с "Basil" на "Kumquat" внутри курсора, курсор вернет "Kumquat". Если же изменение было внесено другим пользователем, курсор по-прежнему будет возвращать "Basil".

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

Динамические курсоры

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

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

Курсоры быстрого доступа

SQL Server поддерживает специальную оптимизированную форму не прокручиваемого курсора, допускающего только чтение. Этот вид курсора объявляется с использованием ключевого слова FAST_FORWARD, и чаще всего его называют "пожарным" курсором (firehose).

"Пожарные" курсоры очень эффективны, но при их использовании имеются два важных ограничения. Во-первых, если в операторе определения SELECT курсора вы использовали столбцы с типом данных text, ntext или image, а также фразу TOP, SQL Server преобразует курсор в ключевой.

Во-вторых, если оператор SELECT, который вы использовали для определения курсора, содержит таблицы, имеющие триггеры, и таблицы, не имеющие триггеров, курсор преобразуется в статический. Триггеры представляют собой сценарии Transact-SQL, которые автоматически исполняются сервером при выполнении для таблицы операторов Data Manipulation Language (DML). Подробнее мы рассмотрим триггеры в уроке 29, сейчас же обратим внимание на следующий момент: если кто-либо добавляет триггер к одной из таблиц, используемых курсором, ваше приложение может внезапно остановить выполнение, поскольку SQL Server преобразует более быстрый курсор в менее быстрый.

Использование курсоров

Использование курсоров подобно использованию локальных переменных – вы объявляете их, устанавливаете значение, а затем используете. Однако, в отличие от локальных переменных, которые автоматически уничтожаются при выходе за пределы области действия, вы должны явным образом освобождать используемые курсором строки, а затем уничтожать курсор.

Создание курсоров

Первым шагом в использовании курсора является его создание. Курсоры Transact-SQL создаются с помощью оператора DECLARE CURSOR.

Внимание! SQL Server поддерживает два различных метода создания курсоров: с использованием синтаксиса SQL-92 и с использованием синтаксиса Transact-SQL. Синтаксис SQL-92 соответствует стандарту ANSI, но имеет меньшие функциональные возможности, чем синтаксис Transact-SQL, который и рассматривается здесь.

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

DECLARE имя_курсора CURSOR

[видимость]

[прокрутка]

[блокировка]

FOR оператор_выборки

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

Видимость курсора определяется с помощью ключевых слов LOCAL или GLOBAL, которые имеют тот же эффект, что и ключевые слова @local_table или @@global_table при объявлении временных таблиц.

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

Параметр прокрутка допускает использование ключевых слов FORWARD_ONLY и SCROLL, которые задают, соответственно, возможность перемещения только от начала к концу, или в любом направлении.

Параметр тип определяет тип создаваемого курсора. Здесь допустимы ключевые слова STATIC, KEYSET, DYNAMIC и FAST_FORWARD. Параметр типа FAST_FORWARD и параметр прокрутки FORWARD_ONLY являются взаимно исключающими.

Параметр блокировка определяет, могут ли строки модифицироваться курсором, и если да, то могут ли их модифицировать другие пользователи. Если используется ключевое слово READ_ONLY, курсор не может вносить никаких изменений в исходные данные. Тем не менее, другие пользователи могут модифицировать данные, либо это можете делать вы сами с помощью оператора UPDATE. Если в качестве параметра блокировка задано SCROLL_LOCKS, обновления могут быть выполнены только курсором. Все другие операторы UPDATE, как внутри этого же пакета, так и предоставляемые другими пользователями, выполняться не будут.

Последняя опция блокировки, OPTIMISTIC, позволяет осуществлять обновления строк как внутри курсора, так и вне его. Это наиболее гибкая опция, но при этом всегда существует вероятность, что модификация, выполненная курсором, окончится неудачей, если строка была изменена после ее чтения курсором.

Параметр TYPE_WARNING предписывает SQL Server отправлять предупреждающее сообщение клиенту, если тип курсора преобразуется от заданного к другому типу. Это возможно, если вы объявляете курсор, который не поддерживает заданный оператор SELECT.

Параметр оператор_выборки, указываемый в фразе FOR, является обязательным. Он задает строки, которые будут включены во множество курсора.

Фраза FOR UPDATE является необязательной. По умолчанию курсоры являются модифицируемыми, если не задан параметр READ_ONLY, однако и в этом случае лучше все-таки использовать эту фразу, чтобы быть уверенным в полученном результате. Вы можете использовать раздел OF имена_столбцов, чтобы указать определенные строки, для которых вы допускаете модификацию. Если вы опускаете раздел OF имена_столбцов, модификация может быть выполнена для всех столбцов, указанных в операторе SELECT.

Курсорные переменные

Transact-SQL позволяет объявлять переменные типа CURSOR. В этом случае стандартный синтаксис DECLARE не создает курсор; вы должны явно установить переменную для курсора с помощью ключевого слова SET.

DECLARE myCursor CURSOR

FOR SELECT OilName FROM Oils

DECLARE @myCursorVariable CURSOR

SET @myCursorVariable = myCursor

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

Вы можете объявить курсорную переменную, а затем использовать ее для непосредственного создания курсора.

DECLARE @myCursorVariable CURSOR SET @myCursorVariable = CURSOR LOCAL FAST_FORWARD FOR SELECT OilName FROM Oils

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

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

Объявление курсора создает объект курсора, но не создает набор записей, которыми курсор будет манипулировать (множество курсора). Множество курсора не создается, пока вы не откроете курсор. После достаточно сложного синтаксиса оператора DECLARE CURSOR, синтаксис оператора кажется вполне прозрачным:

OPEN курсор_или_переменная

Ключевое слово GLOBAL помогает избежать конфликтов: если курсор, объявленный с ключевым словом LOCAL, и курсор, объявленный с ключевым словом GLOBAL, имеют одинаковый идентификатор, ссылки на курсор будут по умолчанию отнесены к локальному курсору, если вы не использовали ключевое слово GLOBAL. Как и в других подобных случаях, лучше явно указывать ключевое слово, если вы открываете глобальный курсор.

Закрытие курсора

Закончив использование курсора, вы должны его закрыть. Оператор CLOSE освобождает ресурсы, используемые для обслуживания множества курсора, а также освобождает все блокировки, наложенные на строки, если вы использовали параметр SCROLLOCKS в операторе DECLARE. Синтаксис команды CLOSE почти идентичен синтаксису оператора OPEN – меняется только ключевое слово:

CLOSE курсор_или_переменная

Освобождение курсора

Последним оператором в последовательности действий, связанных с созданием курсора, является оператор DEALLOCATE. Синтаксис его также прост:

DEALLOCATE курсор_или_переменная

Однако здесь есть одна тонкость: оператор DEALLOCATE удаляет идентификатор или курсорную переменную, но он не обязательно удаляет сам курсор. Сам курсор не удаляется до тех пор, пока все ссылающиеся на него идентификаторы будут либо освобождены, либо перестанут действовать (при выходе за пределы области действия). Рассмотрим следующие примеры:

Создание курсора DECLARE myCursor CURSOR KEYSET READ_ONLY FOR SELECT * FROM Oils -- Создание курсорной переменной DECLARE @cursorVariable CURSOR -- Создание множества записей курсора OPEN myCursor -- Назначение переменной курсору SET @cursorVariable = myCursor -- Освобождение курсора DEALLOCATE myCursor

После освобождения курсора идентификатор myCursor больше не ассоциируется с множеством курсора, но поскольку на множество курсора еще ссылается переменная @cursorVariable, курсор и множество курсора не освобождаются. Если вы явно не освободите также и курсорную переменную, курсор и множество курсора будут существовать, пока переменная не утратит свое действие.

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

Курсоры сами по себе не вызвали бы никакого интереса, если бы вы не могли осуществлять с их помощью определенные действия. Transact-SQL поддерживает три различные команды для работы с курсорами: FETCH, UPDATE и DELETE.

Команда FETCH извлекает указанную строку из множества строк курсора. В своем простейшем варианте команда FETCH имеет следующий синтаксис:

FETCH курсор_или_переменная

В этом формате записи возвращается строка в позиции курсора (текущая строка).

Используйте простую команду FETCH

  1. Перейдите к папке SQL 2000 Step by Step в корневой директории, выделите сценарий с именем SimpleCursor и нажмите кнопку Open (Открыть).
  2. Query Analyzer загрузит сценарий в окно Query (Запрос).

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

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

FETCH курсор_или_переменная INTO список_переменных

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

Выберите строки с записью ее в переменные

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

Эти ключевые слова дают вам возможность задавать абсолютную позицию во множестве курсора. Ключевые слова FIRST и LAST возвращают первую и последнюю строки соответственно, в то время как ABSOLUTE n задает строку, отстоящую на n строк от начала (если n положительно) или от конца (если n отрицательно) множества записей курсора. Значение n может быть выражено в виде константы (3) или в виде переменной (@theRow).

Выберите строки по их абсолютной позиции

  1. Выделите сценарий с именем FetchAbsolute и нажмите кнопку Open (Открыть). Query Analyzer загрузит сценарий в окно Query (Запрос).

Помимо ключевых слов, дающих возможность извлекать строки по их абсолютной позиции, оператор FETCH предусматривает три ключевых слова, позволяющих извлекать строки по их позиции относительно текущей строки. Оператор FETCH NEXT возвращает следующую строку, оператор FETCH PRIOR возвращает предыдущую строку, а оператор FETCH RELATIVE n возвращает строку, отстоящую на n строк от текущей. Подобно оператору FETCH ABSOLUTE n, оператор FETCH RELATIVE n может задавать строки, предшествующие текущей, если n отрицательно, и строки, следующие за текущей, если n положительно.

Выберите строки по их относительной позиции

  1. Выделите сценарий с именем FetchRelative и нажмите кнопку Open (Открыть). Query Analyzer загрузит сценарий в окно Query (Запрос).

Если курсор имеет тип FORWARD_ONLY или PAST_FORWARD, для указания позиции может быть использовано только ключевое слово NEXT. В действительности, если курсор относится к одному из этих типов, ключевое слово NEXT не нужно. SQL Server предполагает, что каждый оператор FETCH фактически представляет собой оператор FETCH NEXT.

Используйте оператор FETCH NEXT для быстродействующего курсора

  1. Выделите сценарий с именем FetchFirehose и нажмите кнопку Open (Открыть). Query Analyzer загрузит сценарий в окно Query (Запрос).

Нажмите кнопку Run Query (Выполнить запрос) в панели инструментов анализатора запросов Query Analyzer. Query Analyzer выполнит запрос.

Модификация и удаление строк через курсоры

Если ваш курсор является модифицируемым. Изменение исходных значений во множестве курсора выполняется достаточно просто. Предусмотрена специальная форма фразы WHERE, которая поддерживает модификацию через курсор:

UPDATE таблица_или_представление SET список_для_модификации WHERE CURRENT OF курсор_или_переменная

Это называется позиционным обновлением. Transact-SQL также поддерживает позиционное удаление, которое имеет следующую форму записи:

DELETE таблица_или_представление WHERE CURRENT OF курсор_или_переменная

Выполните позиционное обновление

  1. Выделите сценарий с именем PositionedUpdate и нажмите кнопку Open (Открыть). Query Analyzer загрузит сценарий в окно Query (Запрос).

Нажмите кнопку Execute Query (Выполнить запрос) в панели инструментов анализатора запросов Query Analyzer. Query Analyzer выполнит запрос. Обратите внимание, что отображаются две панели сетки. Первая создается оператором FETCH и содержит начальное содержимое столбцов. Вторая является результатом выполнения оператора SELECT и содержит значение поля Description после модификации.

Мониторинг курсоров Transact-SQL

Transact-SQL предоставляет две глобальные переменные и функцию, которые помогают вам контролировать работу и состояние вашего курсора. Переменная @@CURSOR_ROWS возвращает количество строк во множестве последнего курсора, открытого в соединении. Значения, возвращаемые @@CURSOR_ROWS, представлены в таблице 1.

Переменная @@FETCH_STATUS возвращает информацию о выполнении последней команды FETCH. В таблице 2 представлены значения, возвращаемые переменной @@FETCH_STATUS.

Наконец, Transact-SQL предоставляет функцию CURSOR_STATUS. Эта функция имеет следующий синтаксис:

CURSOR_STATUS(тип, курсор_или_переменная) Тип может иметь значения "local", "global" или "variable", а курсор_или_переменная – это идентификатор курсора или курсорной переменной, информацию о котором требуется получить. Результаты, возвращаемые функцией CURSOR_STATUS, представлены в таблице 3.

Используйте функции мониторинга курсором

  1. Выделите сценарий StatusFunctions и нажмите кнопку Open (Открыть). Query Analyzer загрузит сценарий в окно Query (Запрос).

Версия для печати

Курсор – это объект, позволяющий по отдельности обрабатывать строки из результирующего набора, возвращенного оператором SELECT. Далее будут рассматриваться курсоры, поддерживаемые в языке Transact- SQL. Это серверные курсоры, существующие как объекты на стороне сервера БД. Бывают также клиентские курсоры, с которыми работают при создании клиентских приложений БД.

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

Работа с курсором обычно включает следующие шаги:

  • объявление курсора;
  • открытие курсора;
  • считывание в переменные значений атрибутов из первой записи курсора;
  • перемещение по курсору (обычно в цикле) и обработка записей курсора;
  • закрытие курсора;
  • освобождение памяти, отведенной курсору.

Объявление курсора выполняется с помощью оператора DECLARE, формат которого представлен ниже. Надо отметить, что в SQL Server этот оператор поддерживает как синтаксис стандарта ISO SQL (версия стандарта в документации нс уточняется), так и синтаксис, использующий набор расширений языка Transact-SQL CURSOR

FOR select_statement

Расширенный синтаксис Transact-SQL:

DECLARE cursor_name CURSOR

FOR select_statement

]][;]

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

"Локальный" курсор, создаваемый по умолчанию или при явном указании LOCAL, доступен только в пакете заданий, хранимой процедуре или триггере, в которых он был создан. Такой курсор неявно освобождается после завершения выполнения пакета, хранимой процедуры или триггера. Исключение составляет случай, когда курсор передается через выходной параметр (OUTPUT) хранимой процедуры. Тогда курсор освобождается при освобождении всех ссылающихся на него переменных или при выходе из "области видимости".

FORWARD_ONLY означает, что "передвигаться" по курсору можно только вперед (доступна только команда FETCH NEXT, см. далее), т.е. каждая запись в курсоре может быть обработана не более одного раза. Если FORWARD ONLY указано без ключевых слов STATIC, KEYSET или DYNAMIC, то курсор работает как курсор DYNAMIC (см. далее). Если не указан ни один из параметров FORWARD_ONLY или SCROLL, а также не указано ни одно из ключевых слов STATIC, KEYSET или DYNAMIC, то по умолчанию задается параметр FORWARD_ONLY.

SCROLL означает, что "передвигаться" по курсору можно в любом направлении (в операторе FETCH доступно FIRST, LAST, PRIOR, NEXT, RELATIVE, ABSOLUTE). Параметр SCROLL не может указываться вместе с параметром FAST_FORWARD. Курсоры STATIC, KEYSET и DYNAMIC имеют значение по умолчанию SCROLL.

STATIC означает, что курсор необновляемый. Результирующий набор данных такого курсора извлекается из БД и сохраняется в базе для временных объектов tempdb. Изменения таблиц, служащих основой для курсора, после этого отображаться в курсоре не будут.

KEYSET – у данного типа курсора набор значений ключей, идентифицирующих отобранные записи, сохраняется во временной таблице. При движении по курсору значения неключевых атрибутов извлекаются из соответствующих таблиц, поэтому изменения в неключевых столбцах будут видны при работе с курсором. Если попавшая в курсор строка к моменту выборки ее оператором FETCH уже удалена из таблицы, служебная переменная @@ FETCH_STATUS вернет значение -2. Строки, добавленные в таблицы после открытия курсора, в курсоре не видны. Если формирующий курсор запрос задействует хотя бы одну таблицу, не имеющую уникального индекса, курсор типа KEYSET преобразуется в тип STATIC.

DYNAMIC – самый "затратный" по потребляемым ресурсам тип курсора, отображающий все изменения данных, сделанные в строках результирующего набора, включая вновь вставленные строки. Значения данных, порядок, а также членство строк в каждой выборке могут меняться. С динамическими курсорами нельзя использовать FETCH ABSOLUTE.

FAST_FORWARD – самый быстродействующий тип курсора, позволяющий перемещаться от одной строки к другой только "вперед". Это тип курсора, принятый по умолчанию (когда необязательные ключевые слова опущены). Он эквивалентен курсору, объявленному с параметрами FORWARD_ONLY и READ_ONLY.

READ_ONLY – определяет курсор "только для чтения": изменения в БД через подобный курсор сделать не удастся.

SCROLL_LOCKS означает, что SQL Server блокирует строки по мере их считывания в курсор, что гарантирует возможность их обновления или удаления через курсор данного типа.

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

TYPE_WARNING указывает, что при неявном преобразовании курсора из запрашиваемого типа к другому (например, описанное выше преобразование курсора KEYSET в STATIC при отсутствии уникального индекса в таблице), клиенту будет отправлено предупреждение.

Select_statement – оператор SELECT, формирующий результирующий набор курсора.

Инструкция FOR UPDATE, определяет обновляемые столбцы в курсоре. Если указано OF column_name [, . . . n], то для изменений будут доступны только перечисленные столбцы. Если списка столбцов нет, обновление возможно для всех столбцов, кроме случая объявления курсора с параметром READ_ONLY.

Чтобы открыть и заполнить курсор, используется команда

OPEN {{ cursor_name} I @cursor_variable)

При открытии, курсор может указываться по имени (cursor_name) или через переменную типа CURSOR (@cursor_variable). Параметр GLOBAL указывает, что cursor_name – это глобальный курсор.

Для перемещения по набору данных курсора и получения данных в виде значений переменных используется оператор FETCH:

FETCH [

{{ cursor_name] I @cursor_variable]

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

Важно отметить, что если курсор только что был открыт, первое выполнение FETCH NEXT приводит к переходу на первую запись курсора.

Таблица 10.10

Навигация по набору данных курсора

Глобальная переменная @@FETCH_STATUS позволяет узнать результат последнего выполнения оператора FETCH:

О – действие выполнено успешно;

  • -1 – выполнение оператора завершилось неудачно, или строка оказалась вне пределов результирующего набора (курсор закончился);
  • -2 – выбираемая строка отсутствует, например если за время работы с курсором "чувствительного к изменениям" типа текущая запись была удалена из БД.

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

CLOSE {{ cursor_name}|@cursor_variable }

Оператор DEALLOCATE удаляет связь между курсором и его именем или переменной. Если это последнее имя или переменная, ссылающаяся на курсор, сам курсор удаляется и освобождаются все используемые им ресурсы:

DEALLOCATE {{ cursor_name] | @cursor_variable) Рассмотрим несложный пример использования курсора. Здесь из таблицы выбираются авторы и названия книг, изданных не ранее 2000 г., после чего данные в цикле выводят операторам SELECT – каждый раз одна запись с собственным заголовком. Дополнительные пояснения даются комментариями в коде:

/*объявляем переменные*/

DECLARE @auth varchar(50), @title varchar(50)

WHERE >= 2000

/*открываем курсор и "пробегаем" его, выводя автора и название отдельным оператором SELECT*/

FETCH NEXT FROM cursorl INTO @auth, @title

WHILE SSFETCH_STATUS = 0

FETCH NEXT FROM cursorl INTO @auth, Stitle

/*закрываем курсор и освобождаем его*/

DEALLOCATE cursorl

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

DECLARE Sauth varchar(50), Stitle varchar(50)

/*объявляем переменную типа курсор*/

DECLARE Scurl CURSOR

DECLARE cursorl CURSOR FAST_FORWARD

SELECT Author, Title FROM dbo.Bookl

WHERE >= 2000

/*присваиваем переменной типа курсор значение*/

SET Scurl = cursorl

WHILE SSFETCH_STATUS = 0

FETCH NEXT FROM Scurl INTO Sauth, Stitle