Transact-SQL 游标名称的作用域

MicrosoftSQL Server 支持在 DECLARE CURSOR 语句中使用 GLOBAL 和 LOCAL 关键字定义游标名称的作用域。GLOBAL 指定游标名称对连接是全局性的。LOCAL 指定游标名称对含有 DECLARE CURSOR 语句的存储过程、触发器或批处理是局部性的。

在 MicrosoftSQL Server 7.0 之前的版本中,Transact-SQL 游标的名称对连接而言是全局性的。可以执行一个存储过程以创建游标,然后调用另一个存储过程从该游标中提取行:

USE AdventureWorks;
GO
CREATE PROCEDURE OpenCrsr AS

DECLARE SampleCrsr CURSOR FOR
SELECT TOP (20)LastName
FROM Person.Contact
WHERE LastName LIKE 'S%';

OPEN SampleCrsr;
GO

CREATE PROCEDURE ReadCrsr AS
FETCH NEXT FROM SampleCrsr
WHILE (@@FETCH_STATUS <> -1)
BEGIN
   FETCH NEXT FROM SampleCrsr
END
GO

EXEC OpenCrsr; /* DECLARES and OPENS SampleCrsr. */
GO
EXEC ReadCrsr; /* Fetches the rows from SampleCrsr. */
GO
CLOSE SampleCrsr;
GO
DEALLOCATE SampleCrsr;
GO

局部游标为存储过程和触发器中实现的游标提供重要的保护作用。全局游标可以在声明它们的存储过程或触发器的外部引用。因此,它们可能会被存储过程或触发器外部的语句无意中更改。因为不能在存储过程外部引用局部游标,所以局部游标比全局游标更安全,除非故意将局部游标作为游标输出参数传递回调用方。

因为可以在存储过程或触发器外部引用全局游标,所以全局游标可能会在无意中影响其他语句。例如,一个存储过程创建了一个名为 xyz 的全局游标,在该存储过程结束时此游标是打开的。当该存储过程完成后,试图使用 xyz 名称声明另一个全局游标的操作将失败,因为出现了重复的名称错误。

全局游标和局部游标有各自的名称空间,因此可以同时具有相同名称的全局游标和局部游标。接受游标名称参数的 Transact-SQL 语句也支持使用 GLOBAL 关键字来标识名称的作用域。如果没有指定 GLOBAL,且同时存在具有在游标名称参数中指定的名称的全局游标和局部游标,那么将引用局部游标。

如果既没有指定 LOCAL 也没有指定 GLOBAL,则由 ALTER DATABASE 语句设置的数据库选项 CURSOR_DEFAULT 控制 DECLARE CURSOR 语句使用的默认值。此数据库选项的当前值存储在 sys.databases 目录视图中。如果 sys.databases 目录视图的 local_cursor_default 列的值为 true,则 Transact-SQL 游标默认为是局部游标。如果此选项为 false,则 Transact-SQL 游标默认为是全局游标。在 SQL Server 中,数据库选项本身的默认值为 FALSE (GLOBAL),以便与 SQL Server 早期版本中的行为保持一致。

使用 DECLARE 和 OPEN 局部游标的存储过程可以将游标传递出去,以供调用的存储过程、触发器或批处理使用。这可以通过使用由新的 CURSOR VARYING 数据类型定义的 OUTPUT 参数来实现。游标变量只能用作 OUTPUT 参数。不能将它们用于输入参数。当存储过程完成时,游标必须是打开的,这样才能将游标返回 OUTPUT 参数。也可以通过新的 CURSOR 数据类型声明局部变量,以包含对局部游标的引用。

USE AdventureWorks;
GO
/* Create a procedure with a cursor output parameter. */
CREATE PROCEDURE OpenCrsr @OutCrsr CURSOR VARYING OUTPUT AS

SET @OutCrsr = CURSOR FOR
SELECT TOP (20) LastName
FROM Person.Contact
WHERE LastName LIKE 'S%';

OPEN @OutCrsr;
GO

/* Allocate a cursor variable. */
DECLARE @CrsrVar CURSOR;

/* Execute the procedure created earlier to fill
  the variable. */
EXEC OpenCrsr @OutCrsr = @CrsrVar OUTPUT;

/* Use the variable to fetch the rows from the cursor. */
FETCH NEXT FROM @CrsrVar
WHILE (@@FETCH_STATUS <> -1)
BEGIN
   FETCH NEXT FROM @CrsrVar
END;

CLOSE @CrsrVar;

DEALLOCATE @CrsrVar;
GO

数据库 API 不支持在存储过程中使用游标输出参数。不能直接从数据库 API 函数中执行包含游标输出参数的存储过程。只能从其他存储过程、触发器、Transact-SQL 批处理或脚本中执行这些存储过程。

只有在显式释放时或关闭连接时 GLOBAL 游标才可用。创建 LOCAL 游标的存储过程、触发器或批处理终止时将隐式释放 LOCAL 游标,除非游标已作为参数返回。当调用存储过程的代码中引用游标的参数或变量超出作用域时,将隐式释放 LOCAL 游标。