游标 CURSOR

游标 CURSOR
一、游标概述
游标(cursor)是数据库系统在内存中开设的一个数据缓冲区,存放SQL语句的执行结果。

每个游标都有一个名字,用户可以用SQL语句逐一从游标中获取记录,并赋给变量做进一步处理。

作用:用于定位结果集的行 和 遍历结果集。

游标产生时间:当执行DML SQL语句时;

游标用于存放:结果集

游标有名字吗:有,SQL或用户给他取名

游标如何操作:用FETCH语句逐一从游标中提取记录,并赋给变量进一步处理

同时可打开几个游标:多个,具体数量由数据库初始化参数OPEN_CURSOR决定。

二、游标分类
游标分为:静态游标(隐式和显式)和REF游标(+游标变量)

  1. 游标的属性
    %FOUND SQL语句影响了一行或多行时为true;

%NOTFOUND SQL语句没有影响任何行时为true(常用,没找到为T,就退出)

%ROWCOUNT SQL语句影响的行数;

%ISOPEN 游标是否打开,始终为false。

  1. 静态游标
    静态游标是指结果集已经确实(静态定义)的游标。

静态游标分:隐式游标 和 显示游标。

2.1 隐式游标
在PL/SQL中隐式游标在执行DML SQL(Insert/Delete/Update/Select)语句时自动创建、自动声明、打开和关闭,其名SQL(注:所有的隐式游标名都叫“SQL”);

示例:

使用游标的属性。

BEGIN

UPDATE emp SET sal=5000 WHERE empno=7369;

IF SQL%FOUND THEN

DBMS_OUTPUT.PUT_LINE( ‘表已更新’ );

END IF;

END;

提示:

在Java中,如对表进行了增删改操作,结果会返回n,表示在数据库中多少行受影响。

我们通过 n>0 或 n=0 来判断SQL代码是否执行成功。

这个n 即SQL%ROWCOUNT属性。

2.2 显示游标
显示游标用于处理SELECT时返回多行的查询;

增删改时不会用显示游标;

显示游标需要手动的去做声明、打开、提取、关闭操作。

示例:

l 显示游标操作

DECLARE

--声明游标:划分存储区域,注意此时并没有执行Select语句。

CURSOR c_name IS

SELECT sal FROM emp WHERE empno=7369;

my_sal emp.sal%TYPE;

BEGIN

--打开游标:执行select语句,获得结果集存到游标中,此时游标指向结果集头,而不是第一条记录。

OPEN c_name;

--提取记录:移动游标取一条记录

FETCH c_name INTO my_sal;

DBMS_OUTPUT.PUT_LINE(my_sal);

--关闭游标:将游标放入缓冲池中,没有完全释放资源,可重新打开。

CLOSE c_name;

END;

l 带参数的显示游标

DECLARE

CURSOR c_name( dno NUMBER ) IS

SELECT * FROM emp WHERE deptno=dno;

my_a emp%ROWTYPE;

BEGIN

OPEN c_name(10); --打开游标,并传值

LOOP

FETCH c_name INTO my_a;

EXIT LOOP c_name%NOTFOUND;

DBMS_OUTPUT.PUT_LINE( '名字:'||my_a.ename );

END LOOP;

CLOSE c_name;

END;

l For循环游标

作用:简化游标处理代码(简化打开、提取、关闭)。

基本语法:

FOR r_index IN cursor_name LOOP

……

END LOOP;

示例:

For循环游标操作

DECLARE

CURSOR c_name IS

SELECT * FROM emp;

BEGIN

FOR i IN c_name LOOP

DBMS_OUTPUT.PUT_LINE( i.ename||' '||i.job );

END LOOP;

END;

注释:如同foreach语句一样,i默认为行类型(%rowtype),自动打开、提取、关闭。

l 带参数的For循环游标

DECLARE

CURSOR c_name( dno number ) IS

SELECT * FROM emp WHERE deptno=dno;

BEGIN

FOR i IN c_name(30) LOOP

DBMS_OUTPUT.PUT_LINE( i.ename||' '||i.job );

END LOOP;

END;

l 游标的嵌套问题

固名思义,就是在游标中嵌套一个游标。

示例:

列出所有部门的人员信息。

要求结果如下:

部门号:10 部门名称:****

编号:** 姓名:**

编号:** 姓名:**

……

部门号:20 部门名称:****

编号:** 姓名:**

编号:** 姓名:**

……

分析:

1.创建部门游标,先将部门信息列出来;

2.在循环取部门信息时,再创建打开员工表游标,进行读取。

代码:

DECLARE

CURSOR mydc IS

SELECT * FROM dept;

mydr dept%ROWTYPE;--声明dept部门表对象

CURSOR myec(dno NUMBER) IS

SELECT * FROM emp WHERE empno=dno;

myer emp%ROWTYPE;--声明emp员工表对象

BEGIN

--先打开部门结果集游标

OPEN mydc;

LOOP

FETCH mydc INTO mydr;

EXIT LOOP mydc%NOTFOUND;--提取完退出LOOP

DBMS_OUTPUT.PUT_LINE( '部门号:'||mydr.deptno||' 部门名称:'||mydr.dname );

--再打开员工结果集游标,入部门编号

OPEN myec(mydr.deptno);

LOOP

FETCH myec INTO myer;

EXIT LOOP myec%NOTFOUND;

DBMS_OUTPUT.PUT_LINE(' 编号:'||myer.empno||' 姓名:'||myer.ename);

END LOOP;

CLOSE myec;

END LOOP;

CLOSE mydc;

END;

–使用For循环简化

DECLARE

CURSOR mydc IS

SELECT * FROM dept;

CURSOR myec(dno number) IS

SELECT * FROM emp WHERE empno=dno;

BEGIN

--遍历部门结果集游标

FOR i INT mydc LOOP

DBMS_OUTPUT.PUT_LINE( '部门号'||i.deptno||' 部门名称:'||i.dname );

--遍历员工结果集游标,传下部门编号

FOR j INT myec(i.detpno) LOOP

DBMS_OUTPUT.PUT_LINE( ' 编号'||j.empno||' 姓名:'||j.ename );

END LOOP;

END LOOP;

END;
  1. REF游标
    REF游标也叫动态游标,动态SQL执行时产生。

REF游标更应该被称之为游标类型,而游标变量则为该类型的游标。

3.1 创建REF游标+游标变量
基本语法:

–创建REF游标

TYPE ref_cursor_name IS REF CURSOR

[RETURN 返回类型];

–声明REF游标变量

游标变量 ref_cursor_name;

示例:

创建个REF游标类型

TYPE ref_name IS REF CURSOR; --定义弱游标类型,名字叫ref_name

c_name ref_name;

3.2 REF游标分类
强类型:带RETURN返回类型

弱类型:不带RETURN返回类型

相对来说,弱类型更为灵活。

示例:

声明强类型REF游标+游标变量

DECLARE

TYPE c_type IS REF CURSOR

RETURN emp%ROWTYPE;

c_name c_type;

3.3 打开游标变量
OPEN cursor_name FOR 查询结果集;

示例:

用REF游标+游标变量显示数据

DECLARE

TYPE my_t IS REF CURSOR

RETURN emp%ROWTYPE; --声明一个游标类型

myc my_t; --定义一个REF类型的游标变量

myr emp%ROWTYPE;

BEGIN

OPEN myc FOR

SELECT * FROM emp;

LOOP

FETCH myc INTO myr;

EXIT WHEN myc%NOTFOUND;

DBMS_OUTPUT.PUT_LINE(myr.ename);

END LOOP;

CLOSE myc;

END;
  1. 标变量的优点和限制
    4.1 游标变量功能强大,可以简化数据处理;

4.2 游标变量优点:

a.可从不同的select语句中提取结果集;

b.可以作为过程的参数进行传递;

c.可以引用游标的所有属性;

d.可以进行赋值运算;

4.3 游标变量的限制:

a.不能在程序包中声明游标变量;

b.for update子句不能与游标变量一起使用;

c.不能使用比较运算符。

三、动态语句拼接 :n
设定一个游标变量来执行动态的sql语句。

数据库管理员、设计师常用的知识点。

基本语法:

OPEN 游标名 FOR ‘SELECT … >:1 …’;

USEING 变量名;

示例1:

OPEN cursor_name FOR

'SELECT * FROM emp WHERE sal>:1 ORDER BY sal DESC'

USEING p_sal;

解释:

–设置占位符:sal>:1动态拼接(相当于JDBC中的?占位符)

–填充占位符:将变量p_sal的值代入SELECT中“:1”位置处

–如有多个动态拼接处,请设置:2,:3,……,同时using 后面变量用逗号分隔。

示例2:

DECLARE

r_emp emp%ROWTYPE;

--定义REF游标及游标变量

TYPE c_type IS REF CURSOR;

cur c_type;

p_salary NUMBER;

BEGIN

p_salary := 2500;

--打开游标

OPEN cur FOR

'select * from emp where sal>:1 order by sal desc'

--填充占位符

USING p_salary;

DBMS_OUTPUT.PUT_LINE('薪水大于'|| p_salary ||'的员工有:');

LOOP

--提取游标中的数据到变量r_emp

FETCH cur INTO r_emp;

EXIT WHEN cur%NOTFOUND;

DBMS_OUTPUT.PUT_LINE('编号:'|| r_emp.empno||'姓名:'||r_emp.ename||'薪水:'||r_emp.sal);

END LOOP;

--关闭游标

CLOSE cur;

END;
<think>好的,用户之前询问了存储过程的算法语句知识点,现在他们想详细了解游标Cursor)的使用。我需要先回顾之前的回答,确保内容连贯,补充更详细的游标信息。 首先,在之前的回答里,我简要提到了游标用于逐行处理结果集,但用户现在需要更深入的内容。我应该详细解释游标的概念、使用场景、基本步骤,以及不同数据库的语法差异,同时给出示例和注意事项。 用户可能的实际需求是在处理需要逐行操作的数据,比如数据迁移、逐行验证或复杂计算。他们可能在使用存储过程时遇到了性能问题,或者在处理大量数据时遇到困难,所以需要了解如何正确使用游标,同时避免常见陷阱。 需要确保回答结构清晰,分步骤说明游标的使用,包括声明、打开、获取、关闭和释放。同时,对比不同数据库(如MySQL、SQL Server、Oracle)的语法差异,这对用户来说很重要,因为他们可能在不同环境中工作。 另外,用户可能对游标的优缺点不太清楚,需要强调游标的性能问题,提供优化建议,比如尽量使用集合操作替代游标。给出实际示例,如数据迁移或逐行处理日志,帮助用户理解应用场景。 还要注意错误处理和资源释放,避免游标未关闭导致的内存泄漏。最后,通过总结强调游标的适用场景和注意事项,确保用户能合理使用游标,避免滥用。</think>### 游标Cursor)的详细使用指南 --- #### **1. 游标是什么?** 游标Cursor)是数据库中用于 **逐行处理查询结果集** 的机制,类似于编程中的迭代器。它允许在存储过程或脚本中逐条访问记录,对每条记录执行特定操作。 - **核心作用**:处理需要逐行操作的复杂业务逻辑(如数据校验、逐行计算、级联更新)。 - **适用场景**:无法通过单条 SQL 语句完成的集合操作,必须逐行处理时使用。 --- #### **2. 游标的使用步骤** 游标的典型生命周期分为 **5 个阶段**: | 步骤 | 描述 | |--------------|----------------------------------------------------------------------| | **声明游标** | 定义游标名称及其关联的查询语句(SELECT)。 | | **打开游标** | 执行查询,将结果集加载到内存中。 | | **逐行获取数据** | 通过 `FETCH` 逐行读取数据,将值赋给变量。 | | **关闭游标** | 释放游标占用的内存资源。 | | **释放游标** | (部分数据库如 SQL Server 需要显式释放) | --- #### **3. 不同数据库的游标语法** --- ##### **3.1 MySQL 中的游标** ```sql -- 声明游标 DECLARE cursor_name CURSOR FOR SELECT column1, column2 FROM table WHERE condition; -- 声明处理程序(用于循环结束时的退出条件) DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; -- 打开游标 OPEN cursor_name; -- 循环读取数据 read_loop: LOOP FETCH cursor_name INTO var1, var2; IF done = 1 THEN LEAVE read_loop; END IF; -- 在此处处理数据(如插入、更新等) END LOOP; -- 关闭游标 CLOSE cursor_name; ``` --- ##### **3.2 SQL Server 中的游标** ```sql -- 声明游标 DECLARE cursor_name CURSOR FOR SELECT column1, column2 FROM table WHERE condition; -- 打开游标 OPEN cursor_name; -- 获取第一行数据 FETCH NEXT FROM cursor_name INTO @var1, @var2; -- 循环读取数据 WHILE @@FETCH_STATUS = 0 BEGIN -- 处理数据 PRINT 'Column1: ' + CAST(@var1 AS VARCHAR) + ', Column2: ' + CAST(@var2 AS VARCHAR); -- 获取下一行 FETCH NEXT FROM cursor_name INTO @var1, @var2; END; -- 关闭释放游标 CLOSE cursor_name; DEALLOCATE cursor_name; ``` --- ##### **3.3 Oracle 中的游标** ```sql -- 显式游标声明 DECLARE CURSOR cursor_name IS SELECT column1, column2 FROM table WHERE condition; v_column1 table.column1%TYPE; v_column2 table.column2%TYPE; BEGIN OPEN cursor_name; LOOP FETCH cursor_name INTO v_column1, v_column2; EXIT WHEN cursor_name%NOTFOUND; -- 退出条件 -- 处理数据(如DBMS_OUTPUT.PUT_LINE输出) END LOOP; CLOSE cursor_name; END; ``` --- #### **4. 游标的类型** - **静态游标(Static Cursor)** 结果集在打开时固定,不反映其他事务的更改(SQL Server 中 `STATIC` 关键字)。 - **动态游标(Dynamic Cursor)** 结果集实时反映其他事务的更改(SQL Server 中 `DYNAMIC` 关键字)。 - **仅向前游标(Forward-Only)** 只能单向逐行读取,不可回滚(MySQL 默认类型)。 --- #### **5. 游标的实际应用示例** ##### **场景:将订单表数据迁移到历史表(MySQL)** ```sql DELIMITER $$ CREATE PROCEDURE migrate_orders() BEGIN DECLARE done INT DEFAULT 0; DECLARE order_id INT; DECLARE order_date DATE; DECLARE cur CURSOR FOR SELECT id, order_date FROM orders WHERE status = 'completed'; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; OPEN cur; read_loop: LOOP FETCH cur INTO order_id, order_date; IF done = 1 THEN LEAVE read_loop; END IF; -- 插入历史表 INSERT INTO orders_history (id, order_date) VALUES (order_id, order_date); END LOOP; CLOSE cur; END $$ DELIMITER ; ``` --- #### **6. 游标的优缺点** | **优点** | **缺点** | |------------------------------|---------------------------------------| | 支持逐行复杂逻辑处理 | 性能低(频繁的上下文切换和 I/O 操作) | | 可访问结果集中的任意行 | 占用内存和锁资源 | | 灵活性高(如动态 SQL 处理) | 代码复杂度高,易出错 | --- #### **7. 注意事项** 1. **尽量少用游标** - 优先使用 `JOIN`、`UPDATE`、`CASE` 等集合操作替代。 - 游标适合处理无法用单条 SQL 实现的场景。 2. **性能优化** - 使用 `FAST_FORWARD`(SQL Server)或 `FOR READ ONLY`(Oracle)等高效游标类型。 - 减少循环内的 SQL 操作次数。 3. **资源释放** - 必须显式关闭游标,避免内存泄漏。 4. **错误处理** - 捕获游标操作中的异常(如 `NO_DATA_FOUND`)。 --- #### **8. 替代方案** - **临时表**:将结果集存入临时表,再用集合操作处理。 - **批量操作**:使用 `BULK COLLECT`(Oracle)或 `LIMIT ... OFFSET`(MySQL)分批次处理。 --- #### **9. 总结** - **游标是处理逐行逻辑的最后手段**,需谨慎使用。 - **核心步骤**:声明 → 打开 → 获取 → 关闭 → 释放。 - **性能关键**:减少循环内的 SQL 操作,优先选择集合操作。 合理使用游标可以解决特定复杂问题,但滥用会导致严重的性能瓶颈。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值