5.9. 游标 (Cursor)

只进游标是 ODBC 中的默认游标类型。

不同的游标具有不同的特征。 最常见的游标类型(称为只进游标)只能通过结果集向前移动。 若要返回到上一行,应用程序必须关闭并重新打开游标,然后从结果集的开头读取行,直到到达所需的行。 只进游标提供了一种快速机制,用于通过结果集进行单一传递。

还有一种叫 可滚动的游标 ,它提供对结果集的随机访问。 此类应用程序还可通过使用所谓的块游标一次提取多行数据来提高性能。

备注

文章中只提及(只进)游标的基本使用。

5.9.1. MySQL 中的 游标

MySQL游标只能用于存储过程(和函数)。

delimiter $$
CREATE PROCEDURE `show_description_all_tables`()
BEGIN
    DECLARE t_name VARCHAR(255) DEFAULT '';
    DECLARE done boolean DEFAULT TRUE;
        -- 定义游标
    DECLARE name_cursor CURSOR FOR
        SELECT table_name FROM information_schema.TABLES 
        WHERE table_schema='employees' AND table_type='base table';
    -- 定义结束条件
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=FALSE;
    -- 遍历查询所有
    OPEN name_cursor;
    -- 获取下一个表名
    FETCH name_cursor INTO t_name;
    WHILE done DO
        -- 获取创建信息
        SET @sql=CONCAT('show create table ',t_name,';');
        -- 预处理
        PREPARE order_sql FROM @sql;
        -- 执行
        EXECUTE order_sql;
        -- 获取下一个表名
        FETCH name_cursor INTO t_name;
    END WHILE;
    -- 释放资源
    DEALLOCATE PREPARE order_sql;
    CLOSE name_cursor;
    DROP TABLE table_result;
END$$
delimiter ;

小技巧

在MySQL的存储过程中判断是否使用游标时考虑的情形:

  1. 是否需要遍历某个结果集(并同时执行一些操作)

    • 如果是则进行下一步,否则不需要使用游标

  2. 判断被遍历的结果是否是一次性的(即该结果集预计在整个存储过程中都不会变化)

    • 如果是则不需要建立临时表以用来存储结果集,直接将该结果集使用于游标定义中。如: DECLARE name_cursor CURSOR FOR SELECT table_name FROM information_schema.TABLES

    • 否则,需要定义一个临时表用以存储数据。

  3. 需要注意定义游标相关变量的顺序

    • 首先,需要定义游标的结束判断的变量,如 : DECLARE done boolean DEFAULT TRUE;

    • 其次,需要定义游标,如: DECLARE name_cursor CURSOR FOR SELECT table_name FROM information_schema.TABLES

    • 最后,需要定义游标的结束时触发的行为,如 : DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=FALSE;

  4. 遍历循环体:

    -- ...
    DECLARE v_name ...;
    DECLARE v_name2 ...;
    DECLARE done boolean DEFAULT TRUE;
    -- ...
    DECLARE cursor_name CURSOR FOR ...;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=FALSE;
    -- ...
    
    -- 遍历查询所有
    OPEN cursor_name;
    FETCH cursor_name INTO v_name,v_name2,...;
    WHILE done DO
        -- ...deal something...
        FETCH cursor_name INTO v_name,v_name2,...;
    END WHILE;
    CLOSE cursor_name;
    -- ...
    

5.9.2. SQL Server 中的 游标

SQL Server 中游标的基本使用相较于 MySQL 来说比较简单。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ALTER PROCEDURE drop_all_tables
AS
BEGIN
    -- 创建表名游标,通过游标来循环遍历
    DECLARE tablesCursor CURSOR FOR 
        SELECT name
    FROM sys.tables
    DECLARE @tableName VARCHAR(255)
    BEGIN
        -- 打开游标
        OPEN tablesCursor
        --将游标向下移1行,获取的数据放入之前定义的变量@tableName
        FETCH NEXT FROM tablesCursor INTO @tableName
        --判断是否成功获取数据
        WHILE @@FETCH_STATUS = 0
        BEGIN
            exec('drop '+@tableName)
            PRINT @tableName
            --将游标向下移1行,获取的数据放入之前定义的变量@tableName
            FETCH NEXT FROM tablesCursor INTO @tableName
        END
        CLOSE tablesCursor
    END
END