在数据库编程中,游标是一种重要的机制,它允许开发者逐行处理查询结果。不同的数据库系统,如SQL Server、Oracle、MySQL和PostgreSQL,提供了各自独特的游标实现方式。本文旨在探讨这些数据库系统中游标操作的相似之处和差异性。
将通过创建一个游标,打开和关闭游标,遍历游标行,根据条件跳过行,以及在需要时中断循环等操作来比较这些数据库系统。
在开始使用游标之前,先创建一些示例数据。
        -- 创建表
        DROP TABLE People;
        CREATE TABLE People (
            FirstName VARCHAR(100),
            LastName VARCHAR(100)
        );
        -- 插入数据
        DELETE FROM People;
        INSERT INTO People VALUES ('Elon', 'Musk');
        INSERT INTO People VALUES ('Den', 'Parker');
        INSERT INTO People VALUES ('Gordon', 'Ramsay'); -- 循环中将跳过此条数据
        INSERT INTO People VALUES ('Bryan', 'Adams'); -- 循环中将中断
        INSERT INTO People VALUES ('Aaron', 'Lord');
        INSERT INTO People VALUES ('Little', 'Alton');
        -- 查询
        SELECT * FROM People ORDER BY FirstName;
    
在SQL Server中,创建游标并查看结果。
        DECLARE @firstName VARCHAR(100), @lastName VARCHAR(100), @fullName VARCHAR(200);
        DECLARE peopleCrs CURSOR FOR SELECT FirstName, LastName FROM People ORDER BY FirstName, LastName;
        OPEN peopleCrs;
        FETCH NEXT FROM peopleCrs INTO @firstName, @lastName;
        SET @fullName = '';
        WHILE @@FETCH_STATUS = 0
        BEGIN
            IF @firstName = 'Gordon' BEGIN BREAK; END -- 退出游标
            IF @firstName = 'Den' BEGIN GOTO Refatch; END -- 忽略此条数据
            SET @fullName = @firstName + ' ' + @lastName;
            PRINT @fullName;
            Refatch: FETCH NEXT FROM peopleCrs INTO @firstName, @lastName;
        END
        CLOSE peopleCrs;
        DEALLOCATE peopleCrs;
    
在Oracle中,创建游标并打印结果。
        DECLARE
            FIRST_NAME VARCHAR(100);
            LAST_NAME VARCHAR(100);
            FULL_NAME VARCHAR(100);
        CURSOR CUR_PEOPLE IS SELECT FirstName, LastName FROM People ORDER BY FirstName, LastName;
        BEGIN
            OPEN CUR_PEOPLE;
            LOOP
                FETCH CUR_PEOPLE INTO FIRST_NAME, LAST_NAME;
                FULL_NAME := '';
                IF CUR_PEOPLE % NOTFOUND THEN EXIT; END IF;
                IF FIRST_NAME = 'Gordon' THEN EXIT; END IF; -- 退出游标
                IF FIRST_NAME = 'Den' THEN CONTINUE; END IF; -- 忽略此条数据
                FULL_NAME := CONCAT(CONCAT(FIRST_NAME, ' '), LAST_NAME);
                DBMS_OUTPUT.put_line(FULL_NAME);
            END LOOP;
            CLOSE CUR_PEOPLE;
        END;
    
MySQL中没有直接打印消息的选项,因此将选择整个结果。
        -- 创建存储过程
        DELIMITER $$
        DROP PROCEDURE IF EXISTS sp_people$$
        CREATE PROCEDURE sp_people()
        BEGIN
            DECLARE first_name VARCHAR(100);
            DECLARE last_name VARCHAR(100);
            DECLARE full_name VARCHAR(200);
            DECLARE result VARCHAR(800);
            DECLARE people_cursor_finished BOOLEAN;
            DECLARE people_cursor CURSOR FOR SELECT firstname, lastname FROM people ORDER BY Firstname ASC;
            DECLARE CONTINUE HANDLER FOR NOT FOUND SET people_cursor_finished = TRUE;
            OPEN people_cursor;
            SET result = '';
            people_cursor_loop: LOOP
                FETCH people_cursor INTO first_name, last_name;
                SET full_name = '';
                IF people_cursor_finished THEN LEAVE people_cursor_loop; END IF;
                IF first_name = 'Gordon' THEN LEAVE people_cursor_loop; END IF; -- 退出游标
                IF first_name = 'Den' THEN ITERATE people_cursor_loop; END IF; -- 忽略此条数据
                SET full_name = CONCAT(first_name, " ", last_name);
                SET result = CONCAT(result, ", ", full_name);
            END LOOP people_cursor_loop;
            CLOSE people_cursor;
            SELECT result;
        END$$
        DELIMITER ;
    
在PostgreSQL中,选择整个结果。
        -- 创建函数
        CREATE OR REPLACE FUNCTION fn_people()
        RETURNS VARCHAR(800) AS $$
        DECLARE
            first_name VARCHAR(100);
            last_name VARCHAR(100);
            full_name VARCHAR(200);
            result_string VARCHAR(800);    
            people_cursor CURSOR FOR SELECT firstname, lastname FROM people ORDER BY Firstname ASC;
        BEGIN
            OPEN people_cursor; 
            result_string := '';
            LOOP
                full_name := '';
                FETCH people_cursor INTO first_name, last_name;
                EXIT WHEN NOT FOUND;
                IF first_name = 'Gordon' THEN EXIT; END IF; -- 退出游标
                IF first_name = 'Den' THEN CONTINUE; END IF; -- 忽略此条数据
                full_name := first_name || ' ' || last_name;
                result_string := result_string || ', ' || full_name;
            END LOOP;
            CLOSE people_cursor;
            RETURN result_string;
        END;
        $$ LANGUAGE plpgsql;
    
不同的数据库版本可能会有所不同。以下是所使用的数据库版本:
        -- SQL Server
        SELECT @@VERSION;
        -- Oracle
        SELECT * FROM V$VERSION;
        -- MySQL
        SELECT VERSION();
        -- PostgreSQL
        SELECT VERSION();