Oracle 游标详解

版权声明:此博客仅用于学习,讨论,欢迎转载(请注明出处!) https://blog.csdn.net/qq_34745941/article/details/81294166

1、概念

1.1 游标是什么?

1、游标是 SQL 的一个 内存工作区
2、由 系统或用户变量 的形式定义。

1.2 游标的作用?

1、用来查询数据库,获取记录集合(结果集)的指针,可以让开发者 一次访问一行结果集, 在每条结果集上作操作。
2、用 ‘牺牲内存’ 来提升 SQL 执行效率,适用于 大数据处理

1.3 游标结构图

这里写图片描述

2、属性

2.1 四个属性

属性 返回值类型 说明
SQL%ROWCOUNT 整型 当前成功执行的数据行数(非总记录数
SQL%ISOPEN 布尔型 游标是否开启, true:开启,false:关闭
SQL%FOUND 布尔型 游标是否有值,true:有,false:没有
SQL%NOTFOUND 布尔型 与上述相反。

3、实例

3.0 基础数据准备

CREATE TABLE stu (
   ID NUMBER(3) PRIMARY KEY,
   xm NVARCHAR2(30)
);
INSERT INTO stu(ID,xm) values(1, '小游子');
INSERT INTO stu(ID,xm) values(2, '小倩子');
INSERT INTO stu(ID,xm) values(3, '小优子');

3.1 静态游标

3.1.1 隐式游标 dml

在 PL/SQL 中使用 DML 和 select into时,会自动创建隐式游标,隐式游标自动声明、打开和关闭,其名为 SQL,通过检查隐式游标的属性可以获得 最近执行的 DML 和 select into 语句的信息

DECLARE
   v_count NUMBER;
BEGIN
   SELECT COUNT(1) INTO v_count FROM stu t; -- 同理 update、insert、delete 一样
   IF SQL%FOUND THEN
      dbms_output.put_line('表 stu 的总记录条数为: '||v_count); -- 3
   END IF;
EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

3.1.2 显示游标 cursor

【1、不带参数】

DECLARE
   v_count NUMBER;
   -- 步骤1:声明游标
   CURSOR  v_sql IS  SELECT COUNT(1) FROM stu t; 
BEGIN
   -- 步骤2:开启游标(无参)
   OPEN v_sql; 
   -- 步骤3:提取一条数据(多条则循环)
   FETCH v_sql INTO v_count;
   -- 步骤4:关闭游标
   CLOSE v_sql;

   dbms_output.put_line('表 stu 的总记录条数为: '||v_count); -- 3 条
EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

【2、带参数】

DECLARE
   v_count NUMBER;
   -- 步骤1:声明游标(带参数)
   CURSOR  v_sql(v_id NUMBER) IS  SELECT COUNT(1) FROM stu t WHERE t.id = v_id; 
BEGIN
   -- 步骤2:开启游标(带参数)
   OPEN v_sql(3); 
   -- 步骤3:提取一条数据(多条则循环)
   FETCH v_sql INTO v_count;
   -- 步骤4:关闭游标
   CLOSE v_sql;

   dbms_output.put_line('表 stu 的总记录条数为: '||v_count); -- 1 条
EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

3.1.3 for 循环简化游标

游标声明、开启、关闭,系统自动处理

DECLARE
   v_count NUMBER;
BEGIN
   FOR i IN (SELECT t.* FROM stu t) LOOP
      dbms_output.put_line(i.id||'对应,姓名是:'||i.xm);
   END LOOP;
EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

3.2 动态游标

3.2.1 自定义类型 ref

【强类型,有 return】

DECLARE
   TYPE type_cur_stu IS REF CURSOR RETURN stu%ROWTYPE;
   v_id    stu.id%TYPE;
   v_xm    stu.xm%TYPE; 
   cur_stu type_cur_stu;
BEGIN
   OPEN cur_stu FOR
      SELECT t.id, t.xm FROM stu t;

   LOOP
      FETCH cur_stu
         INTO v_id, v_xm; -- 严格与 上述 select 部分 "一一对应"
      EXIT WHEN cur_stu%NOTFOUND;
      dbms_output.put_line('当前记录数:' || cur_stu%ROWCOUNT || chr(10) ||
                           '序号:' || v_id || chr(10) || 
                           '姓名:' || v_xm||chr(10) );
   END LOOP;

EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

【弱类型,无 return】,效果同上

DECLARE
   TYPE type_cur_stu IS REF CURSOR;
   v_id      stu.id%TYPE;
   v_xm      stu.xm%TYPE;
   v_sql     VARCHAR(4000);
   cur_stu type_cur_stu;

BEGIN

   v_sql := 'SELECT t.id, t.xm FROM stu t'; -- 常用手法 ←_←

   OPEN cur_stu FOR v_sql; -- 请注意,此时 v_sql 是 “字符串类型”, 不是上述中 return 的类型哦

   LOOP
      FETCH cur_stu
         INTO v_id, v_xm; -- 严格与 上述 select 部分 "一一对应"
      EXIT WHEN cur_stu%NOTFOUND;
       dbms_output.put_line('当前记录数:' || cur_stu%ROWCOUNT || chr(10) ||
                            '序号:' || v_id || chr(10) || 
                            '姓名:' || v_xm||chr(10) );
   END LOOP;

EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

3.2.2 系统类型

系统类型 = 弱类型 的自定义类型 ref

DECLARE
   --TYPE type_cur_stu IS REF CURSOR;
   v_id      stu.id%TYPE;
   v_xm      stu.xm%TYPE;
   v_sql     VARCHAR(4000);
   --cur_stu type_cur_stu;
   cur_stu SYS_REFCURSOR;

BEGIN

   v_sql := 'SELECT t.id, t.xm FROM stu t'; -- 常用手法 ←_←

   OPEN cur_stu FOR v_sql; -- 请注意,此时 v_sql 是 “字符串类型”, 不是上述中 return 的类型哦

   LOOP
      FETCH cur_stu
         INTO v_id, v_xm; -- 严格与 上述 select 部分 "一一对应"
      EXIT WHEN cur_stu%NOTFOUND;
       dbms_output.put_line('当前记录数:' || cur_stu%ROWCOUNT || chr(10) ||
                            '序号:' || v_id || chr(10) || 
                            '姓名:' || v_xm||chr(10) );
   END LOOP;

EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

3.2.3 using

绑定变量,灵活,利用 Oracle 库缓存,优化常用手段

DECLARE
   v_id      stu.id%TYPE;
   v_xm      stu.xm%TYPE;
   v_sql     VARCHAR(4000);
   cur_stu SYS_REFCURSOR;

BEGIN

   v_sql := 'SELECT t.id, t.xm 
               FROM stu t 
              WHERE t.id >= :1 
                AND t.id <= :2'; -- 变量名 1, 2 顺序随意

   OPEN cur_stu FOR v_sql USING 13; -- 这才是变量真正顺序! 

   LOOP
      FETCH cur_stu
         INTO v_id, v_xm; 
      EXIT WHEN cur_stu%NOTFOUND;
       dbms_output.put_line('当前记录数:' || cur_stu%ROWCOUNT || chr(10) ||
                            '序号:' || v_id || chr(10) || 
                            '姓名:' || v_xm||chr(10) );
   END LOOP;

EXCEPTION
   WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
END;

4、效率

4.1 三种游标循环效率对比

结论:一般来说批量处理的速度要最好,隐式游标的次之,单条处理的最差

1、单条处理
open  游标;
loop
   fetch 游标 into 变量;
   exit when 条件
end loop;
close 游标;

2、批量处理
open 游标;
fetch 游标 bulk collect into 集合变量(也就是 table 类型哦) limit 数值;
close 游标;

3、隐式游标
for x in (sql 语句) loop
... 逻辑处理
end loop;

猜你喜欢

转载自blog.csdn.net/qq_34745941/article/details/81294166