@@CURSOR ROWS: примеры (SQL)

Применение функции @@CURSOR_ROWS в SQL Server
Раздел: Функции работы с курсорами
@@CURSOR_ROWS: int

Описание функции @@CURSOR_ROWS

Системная функция @@CURSOR_ROWS в MS SQL Server возвращает количество строк, доступных в последнем открытом курсоре в текущем соединении. Ее вызов не требует аргументов. Функция полезна для определения примерного или точного количества записей до начала их последовательной обработки.

Использование функции актуально после выполнения оператора OPEN для курсора. Возвращаемое значение зависит от типа курсора и этапа его обработки:

  • -m: Курсор асинхронно заполняется, и 'm' представляет собой количество строк, на данный момент извлеченных в него. Возвращается для динамических курсоров (INSENSITIVE или KEYSET), где итоговое число строк может меняться.
  • -1: Курсор является динамическим. Общее число строк неизвестно, так как изменения в базовых таблицах могут отражаться на составе курсора. Данное значение также возвращается, если курсор еще не открыт, был закрыт или деаллоцирован.
  • 0: Не было открытых курсоров, последний открытый курсор не содержал строк, либо он уже был закрыт.
  • n: Полное количество строк в курсоре (где n > 0). Это значение возвращается для статических (STATIC) и наборов ключей (KEYSET) курсоров после их полного асинхронного заполнения.
  • -2 и -3: Указывают на ошибки или несуществующий курсор. Значение -2 означает, что курсор не существует, а -3 указывает, что операция с курсором завершилась с ошибкой, например, при возникновении проблем с созданием набора ключей.

Базовые примеры использования

Простые примеры демонстрируют различные возвращаемые значения функции.

Пример 1: Статический курсор (точное количество)

DECLARE employee_cursor CURSOR STATIC FOR
SELECT EmployeeID FROM Employees;
OPEN employee_cursor;
SELECT @@CURSOR_ROWS AS 'Количество строк';
CLOSE employee_cursor;
DEALLOCATE employee_cursor;
Количество строк
------------------
100

Пример 2: Курсор до открытия и после закрытия

DECLARE test_cursor CURSOR FOR SELECT 1;
SELECT @@CURSOR_ROWS AS 'До открытия';
OPEN test_cursor;
SELECT @@CURSOR_ROWS AS 'После открытия';
CLOSE test_cursor;
SELECT @@CURSOR_ROWS AS 'После закрытия';
DEALLOCATE test_cursor;
До открытия
-------------
-1

После открытия
----------------
1

После закрытия
---------------
-1

Пример 3: Динамический курсор (значение -1)

DECLARE dynamic_cursor CURSOR DYNAMIC FOR
SELECT ProductName FROM Products;
OPEN dynamic_cursor;
SELECT @@CURSOR_ROWS AS 'Динамический курсор';
CLOSE dynamic_cursor;
DEALLOCATE dynamic_cursor;
Динамический курсор
--------------------
-1

Альтернативные методы в MS SQL

В MS SQL Server существуют иные подходы для получения информации о курсорах:

  • Системная хранимая процедура sp_describe_cursor: Предоставляет детальную информацию об указанном курсоре, включая его статус, тип и количество строк. Поле fetch_status может указывать на доступность строк. Процедура более информативна, чем @@CURSOR_ROWS, но сложнее в использовании.
  • Динамические административные представления (DMV): Например, sys.dm_db_cursor_stats дает агрегированную статистику по производительности курсоров на уровне экземпляра, но не показывает количество строк в конкретном открытом курсоре.
  • ROWCOUNT: Переменная @@ROWCOUNT возвращает количество строк, затронутых последним оператором. Она не связана напрямую с курсорами, но может использоваться после оператора FETCH для проверки успешности извлечения строки.

Функцию @@CURSOR_ROWS предпочтительнее использовать для быстрой проверки примерного или точного количества строк в только что открытом курсоре. Альтернативные методы применяются, когда нужна подробная диагностика или статистика.

Аналоги в других СУБД

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

MySQL

-- Аналога прямой функции нет. Часто используется FOUND_ROWS()
-- после SELECT SQL_CALC_FOUND_ROWS, но это для лимитированных запросов.
-- Для курсоров обычно применяют счетчик в цикле FETCH.
DECLARE cnt INT DEFAULT 0;
DECLARE cur CURSOR FOR SELECT id FROM t;
OPEN cur;
FETCH cur INTO ...;
WHILE (condition) DO
SET cnt = cnt + 1;
FETCH cur INTO ...;
END WHILE;
SELECT cnt;

Oracle

-- Используется атрибут %ROWCOUNT курсорной переменной.
DECLARE
CURSOR cur IS SELECT employee_id FROM employees;
v_count NUMBER;
BEGIN
OPEN cur;
v_count := cur%ROWCOUNT; -- Будет 0 после OPEN
FETCH cur INTO ...;
v_count := cur%ROWCOUNT; -- Будет 1 после первого FETCH
CLOSE cur;
END;

PostgreSQL

-- В PL/pgSQL можно использовать GET DIAGNOSTICS после MOVE.
DECLARE
cur_ref CURSOR FOR SELECT * FROM table1;
row_count INTEGER;
BEGIN
OPEN cur_ref;
MOVE FORWARD ALL IN cur_ref; -- Перемещает все строки
GET DIAGNOSTICS row_count = ROW_COUNT;
-- row_count содержит количество строк
CLOSE cur_ref;
END;

SQLite

SQLite не поддерживает серверные курсоры в классическом понимании. Количество затронутых строк можно получить с помощью функции changes() после операторов модификации или путем выполнения отдельного запроса COUNT(*).

Типичные ошибки

Распространенные ошибки связаны с неправильной интерпретацией возвращаемых значений и моментом вызова функции.

Ошибка 1: Проверка количества до открытия курсора

DECLARE my_cursor CURSOR FOR SELECT Name FROM Products;
-- Курсор объявлен, но не открыт
IF @@CURSOR_ROWS > 0
PRINT 'Строки есть';
ELSE
PRINT 'Строк нет или курсор не открыт';
-- Всегда выполнится ветка ELSE, так как функция вернет -1.
Строк нет или курсор не открыт

Ошибка 2: Непонимание значений -1 и 0 для динамических курсоров

DECLARE dyn_cursor CURSOR DYNAMIC FOR SELECT ID FROM LargeTable;
OPEN dyn_cursor;
-- Для DYNAMIC курсора @@CURSOR_ROWS часто возвращает -1,
-- что означает 'неизвестно' или 'изменяемо', а не 'пусто'.
IF @@CURSOR_ROWS = 0
PRINT 'Таблица пуста'; -- Это сообщение может быть неверным.
CLOSE dyn_cursor;
DEALLOCATE dyn_cursor;

В этом случае правильнее после OPEN выполнить FETCH и проверить @@FETCH_STATUS.

История изменений

Функция @@CURSOR_ROWS присутствует в MS SQL Server многие версии без существенных изменений в своем поведении или синтаксисе. В последних версиях SQL Server корпорация Майкрософт не анонсировала значимых модификаций для этой функции. Однако, общая рекомендация заключается в минимизации использования серверных курсоров в пользу операций, основанных на множествах, из-за их влияния на производительность. Альтернативы, такие как оконные функции и временные таблицы, часто являются более эффективными.

Расширенные примеры

Пример 1: Использование в хранимой процедуре с логикой

Пример sql
CREATE PROCEDURE ProcessEmployees
AS
BEGIN
DECLARE @EmpID INT, @RowCount INT;
DECLARE emp_cursor CURSOR STATIC FOR
SELECT EmployeeID FROM Employees WHERE Active = 1;

OPEN emp_cursor;
SET @RowCount = @@CURSOR_ROWS;

IF @RowCount = 0
BEGIN
PRINT 'Нет активных сотрудников для обработки.';
CLOSE emp_cursor;
DEALLOCATE emp_cursor;
RETURN;
END;

PRINT 'Начинается обработка ' + CAST(@RowCount AS VARCHAR) + ' записей.';

FETCH NEXT FROM emp_cursor INTO @EmpID;
WHILE @@FETCH_STATUS = 0
BEGIN
-- Логика обработки каждой записи
PRINT 'Обработка сотрудника ID: ' + CAST(@EmpID AS VARCHAR);
FETCH NEXT FROM emp_cursor INTO @EmpID;
END;

CLOSE emp_cursor;
DEALLOCATE emp_cursor;
END;

Пример 2: Сравнение STATIC и KEYSET курсоров

Пример sql
-- Сессия 1
BEGIN TRANSACTION;
UPDATE Employees SET Salary = Salary * 1.1 WHERE EmployeeID = 1;

-- Сессия 2
DECLARE static_cur CURSOR STATIC FOR SELECT Salary FROM Employees;
DECLARE keyset_cur CURSOR KEYSET FOR SELECT Salary FROM Employees;

OPEN static_cur;
OPEN keyset_cur;

SELECT 'STATIC', @@CURSOR_ROWS FROM static_cur; -- Покажет старое значение Salary для ID=1
SELECT 'KEYSET', @@CURSOR_ROWS FROM keyset_cur; -- Покажет обновленное значение (после COMMIT в сессии 1)

CLOSE static_cur; CLOSE keyset_cur;
DEALLOCATE static_cur; DEALLOCATE keyset_cur;

Пример 3: Асинхронное заполнение и значение -m

Пример sql
-- При большом объеме данных и настройках асинхронного курсора
DECLARE big_cursor CURSOR
STATIC FOR SELECT * FROM VeryLargeTable
FOR READ ONLY
WITH OPTION (FAST_FORWARD); -- FAST_FORWARD часто заполняется синхронно

-- Для демонстрации асинхронного поведения может потребоваться
-- специальная настройка сервера или другой тип курсора (например, INSENSITIVE).
-- Сразу после OPEN функция может вернуть -m.
OPEN big_cursor;

-- В цикле можно отслеживать прогресс
WHILE @@CURSOR_ROWS < 0
BEGIN
WAITFOR DELAY '00:00:01';
PRINT 'Прогресс заполнения: ' + CAST(-@@CURSOR_ROWS AS VARCHAR) + ' строк...';
END

PRINT 'Заполнение завершено. Всего строк: ' + CAST(@@CURSOR_ROWS AS VARCHAR);

MS SQL @@CURSOR_ROWS function comments

En
@@CURSOR ROWS Returns the number of qualifying rows currently in the last cursor opened on the connection