版权声明:此博客仅用于学习,讨论,欢迎转载(请注明出处!) 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 1, 3; -- 这才是变量真正顺序!
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;