Oracle 游标详解 【转载至CSDN「鱼丸丶粗面」】
作者:互联网
文章目录 1、概念 1.1 游标是什么? 1.2 游标的作用? 1.3 游标结构图 1.4 基础数据准备 2、语法及属性 2.1 语法 2.1.1 静态游标 2.1.2 动态游标 2.2 属性 2.2.1 特别说明 sql%notfound 3、分类 3.1 静态游标 3.1.1 隐式游标 dml 3.1.2 显示游标 cursor 3.2 动态游标 3.2.1 自定义类型 ref 3.2.2 系统类型 SYS_REFCURSOR 4、效率 4.1 三种游标循环效率对比 1、概念 1.1 游标是什么? 位于内存中的 "临时表"。 具体如下:游标是从数据表中提取出来的数据,以 临时表 的形式存放到 内存中,在游标中有一个 数据指针, 在初始状态下指向的是首记录,利用 fetch 语句可以移动该指针,从而对游标中的数据进行各种操作,然后将操作结果写回到数据库中。
1.2 游标的作用? 1、用来查询数据库,获取记录集合(结果集)的指针,可以让开发者 一次访问一行结果集, 在每条结果集上作操作。 2、用 ‘牺牲内存’ 来提升 SQL 执行效率,适用于 大数据处理。 1.3 游标结构图
1.4 基础数据准备 DROP TABLE stu PURGE; -- if exists -------------- 清空表,方便测试 ------------------ CREATE TABLE stu ( s_id NUMBER(3), s_xm VARCHAR2(30) ); ALTER TABLE stu ADD CONSTRAINT pk_stu_id PRIMARY KEY (s_id) ; INSERT ALL INTO stu(s_id, s_xm) VALUES (1, '小游子') INTO stu(s_id, s_xm) VALUES (2, '小优子') SELECT 1 FROM dual; -- 循环插入的次数 COMMIT; 2、语法及属性 2.1 语法 2.1.1 静态游标 总共 4 个步骤,缺一不可:(参数可选) DECLARE CURSOR cur_stu(参数值 参数类型) IS SELECT * FROM stu t [WHERE t.id = 参数值]; -- 步骤1: 声明游标 v_stu cur_stu%ROWTYPE; BEGIN OPEN cur_stu(参数值); -- 步骤2: 打开游标 LOOP FETCH cur_stu INTO v_stu; -- 步骤3: 提取数据 EXIT WHEN cur_stu%NOTFOUND; dbms_output.put_line(v_stu.s_id ||' : '||v_stu.s_xm); END LOOP; CLOSE cur_stu; -- 步骤4: 关闭游标 EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END; 2.1.2 动态游标 DECLARE v_sql VARCHAR(2000); v_b1 NUMBER(3) := 3; v_id system.stu.s_id%TYPE; v_xm system.stu.s_xm%TYPE; -- TYPE cur_stu_type IS REF CURSOR; -- cur_stu cur_stu_type; cur_stu SYS_REFCURSOR; BEGIN v_sql := 'SELECT t.s_id, t.s_xm FROM stu t WHERE t.s_id <= :b1'; OPEN cur_stu FOR v_sql USING v_b1; -- 绑定变量 : 大数据处理常用优化手段 LOOP FETCH cur_stu INTO v_id, v_xm; EXIT WHEN cur_stu%NOTFOUND; dbms_output.put_line('序号:' || v_id || chr(10) || '姓名:' || v_xm); END LOOP; CLOSE cur_stu; EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END; 2.2 属性 属性 返回值类型 说明 SQL%ISOPEN 布尔型 游标是否开启, true:开启,false:关闭 SQL%FOUND 布尔型 前一个 fetch 语句是否有值,true:有,false:没有 SQL%NOTFOUND 布尔型 与上述相反,常被用于 退出循环,true:有,false:没有, null : 空。注意哦,只有 为 true 时,才退出(当 第一次 fetch 为 null 时,不会退出!) SQL%ROWCOUNT 整型 当前成功执行的数据行数(非总记录数)
DECLARE CURSOR cur_stu IS SELECT * FROM stu; v_stu cur_stu%ROWTYPE; BEGIN OPEN cur_stu; LOOP FETCH cur_stu INTO v_stu; EXIT WHEN cur_stu%NOTFOUND; -- sql%notfound IF cur_stu%FOUND THEN -- sql%found dbms_output.put_line(v_stu.s_id || ' : ' || v_stu.s_xm); dbms_output.put_line('当前记录条数:' || cur_stu%ROWCOUNT); -- sql%rowcount ELSE dbms_output.put_line('无记录...'); END IF; END LOOP; CLOSE cur_stu; EXCEPTION WHEN OTHERS THEN IF cur_stu%ISOPEN THEN -- sql%isopen CLOSE cur_stu; END IF; dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END; 2.2.1 特别说明 sql%notfound Oracle 官方文档解释:Before the first fetch%NOTFOUND returns NULL. If fetch never executes susscessfully. the loop is never exited, because then EXIT WHEN statement executes only if it’s WHEN condition is true. To be safe. you might want to use the following EXIT statement instead: EXIT WHEN SQL%NOTFOUND OR SQL%NOTFOUND IS NULL; DECLARE CURSOR cur_stu IS SELECT * FROM stu; v_stu cur_stu%ROWTYPE; BEGIN OPEN cur_stu; LOOP -- 注意哦,第一次时 cur_stu 下的记录 初始值均为 null,因为还未提取数据 -- 还有,最后一次时 cur_stu 下的记录 也为 null,因为 数据已经提取完毕,且 fetch .. 位于 sql%notfound 之后! EXIT WHEN cur_stu%NOTFOUND; FETCH cur_stu INTO v_stu; -- before the first fetch... dbms_output.put_line(v_stu.s_id || ' : ' || v_stu.s_xm); END LOOP; CLOSE cur_stu; EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END; 测试结果:
疑惑 / 解释: 总共只有 2 条记录,我们都 fetch 之后,按理说游标已经空了,那么第三次应该是 fetch 的空值,为什么还是输出 ‘2 : 小优子’ 呢? 1、fetch … into 语句有数据时,会覆盖 into 变量后的值 2、fetch … into 语句无数据时,into 变量的值不改变(为最后一次有数据的值),就像 select … into 如果没有数据会报异常,但是不会把 into 后面的变量置为空一样。 可参考: DECLARE CURSOR cur_stu IS SELECT s_xm FROM stu WHERE 1 = 2; -- 限制 无记录 v_stu system.stu.s_xm%TYPE; BEGIN OPEN cur_stu; v_stu := '小倩子'; FETCH cur_stu INTO v_stu; dbms_output.put_line('此时的 v_stu: '||v_stu); CLOSE cur_stu; EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END; 测试结果 :
3、分类 3.1 静态游标 3.1.1 隐式游标 dml 在 PL/SQL 中使用 DML 和 select into时,会自动创建隐式游标,隐式游标自动声明、打开和关闭(无法手动查看),其名为 SQL,通过检查隐式游标的属性可以获得 最近执行的 DML 和 select into 语句的信息 DECLARE v_count NUMBER; BEGIN INSERT INTO stu(s_id, s_xm) VALUES(3, '小倩子'); IF SQL%FOUND THEN dbms_output.put_line('插入成功!'); END IF; UPDATE stu t SET t.s_xm = '小王子' WHERE t.s_id = 3; IF SQL%FOUND THEN dbms_output.put_line('更新成功!'); END IF; DELETE FROM stu t WHERE t.s_id = 3; IF SQL%FOUND THEN dbms_output.put_line('删除成功!'); END IF; SELECT COUNT(1) INTO v_count FROM stu t; IF SQL%FOUND THEN dbms_output.put_line('总记录为: '||v_count); END IF; IF SQL%ISOPEN THEN dbms_output.put_line('可手动查看'); ELSE dbms_output.put_line('无法手动查看'); END IF; EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END;
3.1.2 显示游标 cursor 参考本页:2.1.1 静态游标(参数可选) 3.2 动态游标 3.2.1 自定义类型 ref 【1、弱类型、无 return】 DECLARE v_sql VARCHAR(2000); v_b1 NUMBER(3) := 3; v_id system.stu.s_id%TYPE; v_xm system.stu.s_xm%TYPE; TYPE cur_stu_type IS REF CURSOR; cur_stu cur_stu_type; BEGIN v_sql := 'SELECT t.s_id, t.s_xm FROM stu t WHERE t.s_id <= :b1'; OPEN cur_stu FOR v_sql USING v_b1; -- 绑定变量 : 大数据处理常用优化手段 LOOP FETCH cur_stu INTO v_id, v_xm; EXIT WHEN cur_stu%NOTFOUND; dbms_output.put_line('序号:' || v_id || chr(10) || '姓名:' || v_xm); END LOOP; CLOSE cur_stu; EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END;
【2、强类型,有 return】 open cur… for 时,有两点需要注意: 1、for 后是 SQL语句(而不能是 字符串) 2、cur… 必须和 return 的 类型完全一致 3、无法使用 绑定变量 DECLARE -- v_sql VARCHAR(2000); -- v_b1 NUMBER(3) := 3; -- v_id system.stu.s_id%TYPE; -- v_xm system.stu.s_xm%TYPE; v_stu system.stu%ROWTYPE; TYPE cur_stu_type IS REF CURSOR RETURN system.stu%ROWTYPE; cur_stu cur_stu_type; BEGIN -- v_sql := 'SELECT t.s_id, t.s_xm FROM stu t WHERE t.s_id <= :b1'; OPEN cur_stu FOR SELECT t.s_id, t.s_xm FROM stu t; LOOP FETCH cur_stu INTO v_stu; --v_id, v_xm; EXIT WHEN cur_stu%NOTFOUND; dbms_output.put_line('序号:' || v_stu.s_id || chr(10) || '姓名:' || v_stu.s_xm); END LOOP; CLOSE cur_stu; EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END; 3.2.2 系统类型 SYS_REFCURSOR 我常用的,写法简洁,效果完全同 弱类型 ref 参考本页:2.1.2 动态游标 4、效率 4.1 三种游标循环效率对比 结论:一般来说批量处理的速度要最好,隐式游标的次之,单条处理的最差 1、批量处理 open 游标; loop fetch 游标 bulk collect into 集合变量(也就是 table 类型哦) limit 数值; -- 一般 500 左右 exit when 条件 --(变量.count = 0,如果用 sql%notfound 不足 limit 的记录就不会被执行哦) close 游标; 2、隐式游标 for x in (sql 语句) loop ... 逻辑处理 end loop; 3、单条处理 open 游标; loop fetch 游标 into 变量; exit when 条件 end loop; close 游标; 实际开发常用: PS: 如果对 table 类型、record 类型有疑问,请点击 %type、%rowtype,varry、record、table 的使用详解 DECLARE v_sql VARCHAR(2000); v_b1 NUMBER(3) := 3; TYPE record_stu IS RECORD( v_id system.stu.s_id%TYPE, v_xm system.stu.s_xm%TYPE); TYPE table_stu IS TABLE OF record_stu; v_stu table_stu; cur_stu SYS_REFCURSOR; BEGIN v_sql := 'SELECT t.s_id, t.s_xm FROM stu t WHERE t.s_id <= :b1'; OPEN cur_stu FOR v_sql USING v_b1; -- 绑定变量 : 大数据处理常用优化手段 LOOP FETCH cur_stu BULK COLLECT INTO v_stu LIMIT 1; -- 数据量太少,仅当前测试使用哦,实际开发 建议 500 左右 EXIT WHEN v_stu.count = 0; FOR i IN v_stu.first .. v_stu.last LOOP dbms_output.put_line('序号:' || v_stu(i).v_id || ' , ' || '姓名:' || v_stu(i).v_xm); END LOOP; END LOOP; CLOSE cur_stu; EXCEPTION WHEN OTHERS THEN dbms_output.put_line(SQLCODE || ' : ' || SQLERRM); dbms_output.put_line(dbms_utility.format_error_backtrace); END;
———————————————— 版权声明:本文为CSDN博主「鱼丸丶粗面」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_34745941/article/details/81294166
标签:dbms,cur,游标,鱼丸,stu,put,output,粗面 来源: https://www.cnblogs.com/zouhao/p/12100196.html