Oracle数据库数据编程SQL<3.3 PL/SQL 游标>

news2025/4/28 1:05:07

 游标(Cursor)是Oracle数据库中用于处理查询结果集的重要机制,它允许开发者逐行处理SQL语句返回的数据。

目录

一、游标基本概念

1. 游标定义

2. 游标分类 

二、静态游标

(一)显式游标

【一】不带参数,普通的显示游标

1. 显式游标使用步骤

2. 语法

3. 显式游标的四个属性

4. 注意事项

5. %notfound 和普通循环一起用

6. %found 和while循环一起用

7. 基本示例

【二】带参数的显示游标

1. 语法结构

2. 示例代码

3. 练习

(二)隐式游标

1.隐式游标的四个属性

2. 示例代码

二、动态游标

【动态游标注意事项】

【强类型游标和弱类型游标区别】

【动态游标类型定义】

(一)强类型游标

(二)弱类型游标(SYS_REFCURSOR)

(三)动态游标

三、游标变量与批量处理

1. 游标变量

2. 批量提取(BULK COLLECT)

3. 批量处理与FORALL

四、游标最佳实践

五、高级游标技术

1. 可更新游标 

2. 游标子查询

3. 游标表达式(12c+)


一、游标基本概念

1. 游标定义

游标是一个指向上下文区域的指针,用于处理SQL语句的执行结果。它提供了以下能力:

  • 逐行访问结果集

  • 跟踪当前处理的行

  • 对结果集进行修改或删除操作

2. 游标分类 

游标类型描述生命周期控制方式
静态隐式游标Oracle自动为每条SQL语句创建    单条SQL执行期间Oracle自动管理
显式游标开发者显式定义从OPEN到CLOSE开发者手动控制
动态REF游标动态游标,运行时确定灵活控制开发者控制

二、静态游标

(一)显式游标

显示的游标:在declare的部分用is显示了的游标

【一】不带参数,普通的显示游标

1. 显式游标使用步骤

(1)声明游标:定义游标及其关联的SELECT语句

(2)打开游标:执行查询,填充结果集

(3)提取数据:从结果集中获取行数据

(4)关闭游标:释放资源

2. 语法
-- 1. 声明游标
CURSOR cursor_name [(parameters)]
    [RETURN return_type]
    IS select_statement;

-- 2. 打开游标
OPEN cursor_name [(parameters)];

-- 3. 提取数据
FETCH cursor_name INTO variable_list;

-- 4. 关闭游标
CLOSE cursor_name;

-- 5. 举例
declare
cursor cur_name is select语句;---声明一个显示游标
begin 
  open cur_name;--打开游标
  fetch cur_name into 变量;--赋值变量,提取记录
  dbms_output.put_line()---打印
  close cur_name;--关闭游标
end;
/

3. 显式游标的四个属性
属性返回值描述说明

cursor_name%FOUND

布尔值

如果最近一次

FETCH返回行则为TRUE

游标的指针是否有值(有)对

(没有) 错

cursor_name%NOTFOUND

布尔值

如果最近一次

FETCH未返回行则为TRUE

游标的指针是否没值(有值)错,

(没值)对,理论上可以返回空

在open之后fetch之前可以返回空

cursor_name%ROWCOUNT

数值

到目前为止已提取的行数

游标的指针已经指了几行,返回数值,

但是要赋给变量才能显示

返回最近一次从游标读取的数据

cursor_name%ISOPEN

布尔值

如果游标已打开则为TRUE

判断是否打开游标(打开)对

(没有)错

4. 注意事项

首先声明一个游标,使用之前先打开游标,提取记录只能一行,可以多列

使用完游标要关闭游标,可以通过open打开游标继续使用

5. %notfound 和普通循环一起用
 open→loop fetch→exit when %notfound→打印→end loop→close

【举例1】
  declare
  cursor cur_a is select * from emp;---声明一个显示游标
  v_emp emp%rowtype;---声明变量
  v1    varchar2(20);---声明变量
begin
  open cur_a;--打开游标
    loop/*普通循环*/                                 
      fetch cur_a  into v_emp;/*赋值变量,抓取记录*/            
      exit when cur_a%notfound;/*和普通循环一起用*/                    
      dbms_output.put_line(v_emp.ename);                              
    end loop;/*结束循环*/                                                
  close cur_a;/*关闭游标*/                      
 end;
/

-- LOOP循环语法 
declare 部分;
begin
     loop 要执行的语句;
     exit when 退出的条件;
     end loop;
end;
注意事项:进入循环不需要条件
6. %found 和while循环一起用
open→fetch→while %found loop→打印→fetch→end loop→close
 【举例】
declare
  cursor cur_a is select * from emp;
  v_emp emp%rowtype;
begin
  open cur_a;
    fetch cur_a into v_emp;
    while cur_a%found loop
    dbms_output.put_line(v_emp.ename);
    fetch cur_a into v_emp;
    end loop;
  close cur_a;
end;
7. 基本示例
DECLARE
    -- 1. 声明游标
    CURSOR emp_cursor IS
        SELECT employee_id, last_name, salary
        FROM employees
        WHERE department_id = 10;
    
    v_emp_id employees.employee_id%TYPE;
    v_name employees.last_name%TYPE;
    v_sal employees.salary%TYPE;
BEGIN
    -- 2. 打开游标
    OPEN emp_cursor;
    
    -- 3. 提取数据
    LOOP
        FETCH emp_cursor INTO v_emp_id, v_name, v_sal;
        EXIT WHEN emp_cursor%NOTFOUND;
        
        DBMS_OUTPUT.PUT_LINE(v_emp_id || ': ' || v_name || ', ' || v_sal);
    END LOOP;
    
    -- 4. 关闭游标
    CLOSE emp_cursor;
END;
/

1、输出emp表工资前十名的员工姓名和薪资
declare
  cursor cur_1 is
    select ename, sal
    from (select ename, sal from emp order by sal desc)
    where rownum <= 10;
  v_n emp.ename%type;
  v_s emp.sal%type;
begin
  open cur_1;
   loop
     fetch cur_1 into v_n, v_s;
     exit when cur_1%notfound;
     dbms_output.put_line('姓名:' || v_n || '薪资:' || v_s);
   end loop;
  close cur_1;
end;
/
-------------------
declare
  cursor cur_1 is
    select ename, sal
    from (select ename, sal from emp order by sal desc)
    where rownum <= 10;
  v_n emp.ename%type;
  v_s emp.sal%type;
begin
  open cur_1;
    fetch cur_1 into v_n, v_s;
    while cur_1%found loop
    dbms_output.put_line('姓名:' || v_n || '薪资:' || v_s);
    fetch cur_1 into v_n, v_s;
    end loop;
  close cur_1;
end;
/

【二】带参数的显示游标

游标可以接受参数,使查询更加灵活:

1. 语法结构
CURSOR cursor_name (parameter1 datatype, parameter2 datatype, ...)IS select_statement;

declare
  cursor cur_name(变量 类型) is select语句;---声明一个显示游标
begin 
  open cur_name(变量);--打开游标
  fetch cur_name into 变量;--赋值变量,提取记录
  close cur_name;--关闭游标
end;
2. 示例代码
DECLARE
    -- 带参数的游标
    CURSOR emp_cursor (p_dept_id NUMBER, p_min_sal NUMBER) IS
        SELECT employee_id, last_name, salary
        FROM employees
        WHERE department_id = p_dept_id
        AND salary >= p_min_sal;
    -- 记录类型变量
    v_emp_record emp_cursor%ROWTYPE;
BEGIN
    -- 打开游标并传入参数
    OPEN emp_cursor(10, 5000);
    LOOP
        FETCH emp_cursor INTO v_emp_record;
        EXIT WHEN emp_cursor%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(v_emp_record.employee_id || ': ' || 
                             v_emp_record.last_name || ', ' || 
                             v_emp_record.salary);
    END LOOP;
    CLOSE emp_cursor;
END;
/

declare
  cursor cur_1(v1 number) is select sal from emp where empno = v1; --声明一个带参数的显示游标
  v_s emp.sal%type; --负责接收的变量
begin
  open cur_1(&a); ---(7788)即为(v1 number)
  fetch cur_1 into v_s;
  dbms_output.put_line(v_s);
  close cur_1;
end;
3. 练习
【1】输出工作是MANAGER的姓名、工资、;工作是SALESMAN的姓名、佣金;
工作是CLERK的姓名、入职日期。
declare
  cursor cur_2(v1 varchar2) is
    select * from emp where job = v1;
  v_emp emp%rowtype;
begin
  open cur_2('MANAGER');---
  loop
    fetch cur_2
      into v_emp;
    exit when cur_2%notfound;
    dbms_output.put_line(v_emp.ename ||' '|| v_emp.sal);
  end loop;
  close cur_2;
  open cur_2('SALESMAN');
  loop
    fetch cur_2
      into v_emp;
    exit when cur_2%notfound;
    dbms_output.put_line(v_emp.ename ||' '|| v_emp.comm);
  end loop;
  close cur_2;
  open cur_2('CLERK');
  loop
    fetch cur_2
      into v_emp;
    exit when cur_2%notfound;
    dbms_output.put_line(v_emp.ename ||' '|| to_char(v_emp.hiredate,'yyyy-mm-dd'));
  end loop;
  close cur_2;
end;
/

【2】输出工作是MANAGER的姓名、工作、;工作是SALESMAN的姓名、佣金;
工作是CLERK的姓名、入职日期。
declare
  cursor cur_xx(v_1 emp.job%type) is
    select * from emp where job = v_1;
  v_emp emp%rowtype;
begin
  open cur_xx('MANAGER');
  loop
    fetch cur_xx
      into v_emp;
    exit when cur_xx%notfound;
    dbms_output.put_line('员工姓名:'||v_emp.ename ||'    '||'职位:'|| v_emp.job);
  end loop;
  close cur_xx;
  open cur_xx('SALESMAN');
  loop
    fetch cur_xx
      into v_emp;
    exit when cur_xx%notfound;
    dbms_output.put_line('员工姓名:'||v_emp.ename ||'    '||'工资:'|| v_emp.sal);
  end loop;
  close cur_xx;
  open cur_xx('CLERK');
  loop
    fetch cur_xx
      into v_emp;
    exit when cur_xx%notfound;
    dbms_output.put_line('员工姓名:'||v_emp.ename ||'    '||'入职日期:'||v_emp.hiredate);
  end loop;
  close cur_xx;
end;
/

-----------------------------------------------------------------------------------------
输出名字中包含%的人
select * from emp where ename like '%'||v1||'%' or ename like'%'||v_2||'%'

【3】打印名字中包含A的人数,包含E的平均工资,包含o的总工资
declare
  cursor cur_3(v1 varchar2) is---先让cur_3有了selecte
    select count(sal), avg(sal), sum(sal)
    from emp
    where ename like '%' || v1 || '%';----注意学习这种方法

--声明一个带参数的显示游标cur_3
  v_2 number;
  v_3 number;
  v_4 number;
----------------添加负责接收的变量
begin
  open cur_3('A');
  loop
    fetch cur_3---又从cur_3里提取值赋值给变量v_2,v_3,v_4
      into v_2, v_3, v_4;
    exit when cur_3%notfound;
    dbms_output.put_line(v_2);
  end loop;
  close cur_3;
  ---------------
  open cur_3('E');
  loop
    fetch cur_3
      into v_2, v_3, v_4;
    exit when cur_3%notfound;
    dbms_output.put_line(v_3);
  end loop;
  close cur_3;
  ----------------
  open cur_3('O');
  loop
    fetch cur_3
      into v_2, v_3, v_4;
    exit when cur_3%notfound;
    dbms_output.put_line(v_4);
  end loop;
  close cur_3;
  ----------------
end;

(二)隐式游标

主要应用于增加删除更新数据,Oracle为每条DML语句自动创建隐式游标,当执行SQL语句的时候,这个游标是处理该语句的工作区域。在使用的时候要使用隐式游标的默认名称SQL。 

1.隐式游标的四个属性

属性返回描述说明
cursor_name%FOUND布尔值如果DML操作影响至少一行返回TRUE

游标的游标中是否有值,返回最近一次的结果,成功(对)否则(错)

cursor_name%NOTFOUND布尔值如果DML操作未影响任何行返回TRUE

游标的游标中是否有值,返回最近一次的结果,成功(错)

cursor_name%ROWCOUNT数值返回DML操作影响的行数

返回最近一次从游标中读取到的记录--数值类型

cursor_name%ISOPEN布尔值对隐式游标总是返回FALSE

判断是否打开游标。永远返回错

 补充:闪回不仅可以闪回删除前的数据,也可以返回之前某一时间点的数据

2. 示例代码

BEGIN
    -- 更新操作
    UPDATE employees SET salary = salary * 1.1 
    WHERE department_id = 10; 
    -- 检查隐式游标属性
    IF SQL%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('更新了 ' || SQL%ROWCOUNT || ' 条记录');
    END IF;
    -- 删除操作
    DELETE FROM temp_employees WHERE employee_id = 9999;
    IF SQL%NOTFOUND THEN
        DBMS_OUTPUT.PUT_LINE('未删除任何记录');
    END IF;
END;
----------------------------------------------------------------
----------------------------------------------------------------
/
begin
  delete from emp001 where deptno=10;--3
  --dbms_output.put_line('删除了'||sql%rowcount||'行');
  delete from emp001 where deptno=20;--5
  if sql%found 
    then
   dbms_output.put_line('删除了'||sql%rowcount||'行') ;
   end if;
  end;
/
----------------------------------------------------------------
begin
  --dbms_output.put_line('删除了'||sql%rowcount||'行');
  delete from emp001 where deptno in (10,20);--5
  if sql%found 
    then
   dbms_output.put_line('删除了'||sql%rowcount||'行') ;
  end if;
end;
/
----------------------------------------------------------------
----------------------------------------------------------------
declare
v_name csm_product.product_name%type;
begin
  insert into test_t values(1);
  if sql%found then
    dbms_output.put_line('收到影响的行数为:'||sql%rowcount);
  end if;
  rollback;
end;
----------------------------------------------------------------
----------------------------------------------------------------
declare
cursor v_cur is select * from test_t
begin
  if v_cur%isopen then
    dbms_output.put_line('游标已经打开');
  else
    dbms_output.put_line('游标未打开');
  end if;
  open v_cur;
  if v_cur%isopen then
    dbms_output.put_line('游标已经打开');
  end if;
  close v_cur;
  insert into test_t valuse(1);
  if sql%found then
    dbms_output.put_line('执行成功,影响的行数:'||sql%rowcount)
  elsif sql%notfound then
    dbms_output.put_line('执行失败');
  end if;
  rollback;
end;

二、动态游标

【动态游标注意事项】

1、使用动态游标必须声明游标类型

2、只要列的格式相同,可以同时打开多表

【强类型游标和弱类型游标区别】

1、强类型游标有return,open时查询得到的结果要和return后的表的数据类型、结构、顺序一致。

2、没有return,open时查询的结果比较自由,fetch的时候into给的变量要和SQL查询的类型结构数量一致。

【动态游标类型定义】

TYPE cursor_type IS REF CURSOR [RETURN return_type];

(一)强类型游标

DECLARE
    TYPE emp_cursor_type IS REF CURSOR RETURN employees%ROWTYPE;
    emp_cursor emp_cursor_type;
    v_emp employees%ROWTYPE;
BEGIN
    OPEN emp_cursor FOR SELECT * FROM employees WHERE department_id = 20;
    
    LOOP
        FETCH emp_cursor INTO v_emp;
        EXIT WHEN emp_cursor%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(v_emp.employee_id || ': ' || v_emp.last_name);
    END LOOP;
    
    CLOSE emp_cursor;
END;
/

declare
  type cur_name_ref is ref cursor return emp%rowtype; --emp可以自定义,只是声明了一个类型
  cur_1 cur_name_ref;
  v_emp    emp%rowtype;
begin
--------------------------------------------------------------------
  open cur_1 for select * from emp;-------------同时打开多表---------
  fetch cur_1
    into v_emp;
  dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
 close cur_1;
--------------------------------------------------------------------
  open cur_1 for select * from emp001;---------同时打开多表----------
  fetch cur_1
    into v_emp;
  dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
  close cur_1;
end;

(二)弱类型游标(SYS_REFCURSOR)

DECLARE
    emp_cursor SYS_REFCURSOR;
    v_emp_id employees.employee_id%TYPE;
    v_emp_name employees.last_name%TYPE;
BEGIN
    -- 打开第一个查询
    OPEN emp_cursor FOR 
        SELECT employee_id, last_name 
        FROM employees 
        WHERE department_id = 10;
    
    DBMS_OUTPUT.PUT_LINE('部门10员工:');
    LOOP
        FETCH emp_cursor INTO v_emp_id, v_emp_name;
        EXIT WHEN emp_cursor%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(v_emp_id || ': ' || v_emp_name);
    END LOOP;
    CLOSE emp_cursor;
    
    -- 重用游标执行不同查询
    OPEN emp_cursor FOR 
        SELECT department_id, department_name 
        FROM departments;
    
    DBMS_OUTPUT.PUT_LINE('所有部门:');
    LOOP
        FETCH emp_cursor INTO v_emp_id, v_emp_name;  -- 重用变量
        EXIT WHEN emp_cursor%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(v_emp_id || ': ' || v_emp_name);
    END LOOP;
    CLOSE emp_cursor;
END;
/


declare
  type cur_name_ref is ref cursor;---没有return,和强类型的区别就在于此
  cur_name cur_name_ref;
  v_emp    emp%rowtype;
begin
  open cur_name for select * from emp;-------------同时打开多表---------------
  fetch cur_name
    into v_emp;
  dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
 close cur_1;---注意游标开一次,关一次,不然一直开着耗内存。
  open cur_name for select * from emp001;--------同时打开多表---------------
  fetch cur_name
    into v_emp;
  dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
  close cur_1;
end;

【练习题】
1、输出工作是MANAGER的姓名、工资;工作是SALESMAN的姓名、佣金;
工作是CLERK的姓名、入职日期。
declare
  type  cur_1_ref is ref cursor; ---没有return
  cur_1 cur_1_ref;
  v1    varchar2(20);
  v2    number;
  v3    date;
begin
  open cur_1 for
    select ename, sal from emp where job = 'MANAGER';
  loop
    fetch cur_1
      into v1, v2;
    exit when cur_1%notfound;
    dbms_output.put_line(v1 || v2);
  end loop;
  open cur_1 for
    select ename, sal from emp where job = 'SALESMAN';
  loop
    fetch cur_1
      into v1, v2;
    exit when cur_1%notfound;
    dbms_output.put_line(v1 || v2);
  end loop;
  open cur_1 for
    select ename, hiredate from emp where job = 'CLERK';
  loop
    fetch cur_1
      into v1, v3;
    exit when cur_1%notfound;
    dbms_output.put_line(v1 || v3);
  end loop;
end;
/

--用弱类型游标,打印emp名字中包含A的人数,dept部门编号的平均数
--salgrade 第三等级的hisal
declare
  type cur_name_ref is ref cursor;
  cur_gam cur_name_ref;
  v_1     number;
begin
  open cur_gam for
    select count(ename) from emp where ename like '%A%';----单列单行不用循环
  fetch cur_gam
    into v_1;
  dbms_output.put_line('名字包含A的人数:' || v_1);
  close cur_gam;
  open cur_gam for
    select avg(deptno) from dept;----单列单行不用循环
  fetch cur_gam
    into v_1;
  dbms_output.put_line('部门编号的平均数:' || v_1);
  close cur_gam;
  open cur_gam for
    select hisal from salgrade where grade = 3;----单列单行不用循环
  fetch cur_gam
    into v_1;
  dbms_output.put_line('第三等级的hisal:' || v_1);
  close cur_gam;
end;
--------如果多行多列,加入循环的写法
declare
  type cur_name_ref is ref cursor;
  cur_gam cur_name_ref;
  v_1 number;
begin
  open cur_gam for
    select count(ename) from emp where ename like '%A%';
  loop
    fetch cur_gam
      into v_1;
    exit when cur_gam%notfound;
    dbms_output.put_line('名字包含A的人数:'||v_1);
  end loop;
  close cur_gam;
-----------------------
  open cur_gam for
  select avg(deptno) from dept;
 loop
    fetch cur_gam
      into v_1;
    exit when cur_gam%notfound;
    dbms_output.put_line('部门编号的平均数:'||v_1);
  end loop;
  close cur_gam;
-----------------------------
   open cur_gam for
  select hisal from salgrade where grade=3;
 loop
    fetch cur_gam
      into v_1;
    exit when cur_gam%notfound;
    dbms_output.put_line('第三等级的hisal:'||v_1);
  end loop;
  close cur_gam;
end;

(三)动态游标

【语法】
declare
  cur_name sys_refcursor;
  v1       number;
  v2       varchar2(20);
begin
  open cur_name for
    select empno, ename from emp where empno = 7788;
  fetch cur_name
    into v1, v2;
  dbms_output.put_line(v1 || v2);
  close cur_name;
--------------
  open cur_name for
    select empno, ename from emp where deptno = 20;
  fetch cur_name
    into v1, v2;
  dbms_output.put_line(v1 || v2);
  close cur_name;
end;

【练习题】
1、打印10部门的人员姓名和部门地址,打印20部门的工资和工资等级
declare
  cur_1 sys_refcursor;
  ve       varchar2(20);
  vc       varchar2(20);
  vs       number;
  vg       number;
begin
  open cur_1 for
    select e.ename,d.loc from emp e,dept d where e.deptno=d.deptno and d.deptno=10;
    loop
  fetch cur_1
    into ve, vc;
    exit when cur_1%notfound;
  dbms_output.put_line(ve ||'  '|| vc);
  end loop;
  open cur_1 for
    select e.sal, s.grade from emp e,salgrade s where e.sal between s.losal and s.hisal and e.deptno=20;
    loop
  fetch cur_1
    into vs, vg;
    exit when cur_1%notfound;
  dbms_output.put_line(vs ||' '|| vg);
  end loop;
  close cur_1;
end;
/

2、用动态游标里面的动态游标,打印emp名字中包含A的人数,dept部门编号的平均数,salgrade 第三等级的hisal
declare
  cur_2 sys_refcursor;
  v1    number;
begin
  open cur_2 for
    select count(ename) from emp where ename like '%A%';
  fetch cur_2
    into v1;
  dbms_output.put_line('名字包含A的人数:' || v1);
  close cur_2;
----------------------
  open cur_2 for
    select avg(deptno) from dept;
  fetch cur_2
    into v1;
  dbms_output.put_line('部门编号的平均数:' || v1);
  close cur_2;
-----------------------
  open cur_2 for
    select hisal from salgrade where grade = 3;
  fetch cur_2
    into v1;
  dbms_output.put_line('第三等级的hisal:' || v1);
  close cur_2;
end;
/

三、游标变量与批量处理

1. 游标变量

DECLARE
    TYPE emp_cursor_type IS REF CURSOR;
    emp_cursor emp_cursor_type;
    
    PROCEDURE process_employees (p_cursor IN emp_cursor_type) IS
        v_emp employees%ROWTYPE;
    BEGIN
        LOOP
            FETCH p_cursor INTO v_emp;
            EXIT WHEN p_cursor%NOTFOUND;
            DBMS_OUTPUT.PUT_LINE(v_emp.employee_id || ': ' || v_emp.last_name);
        END LOOP;
    END;
BEGIN
    OPEN emp_cursor FOR SELECT * FROM employees WHERE department_id = 10;
    process_employees(emp_cursor);
    CLOSE emp_cursor;
END;
/

2. 批量提取(BULK COLLECT)

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, last_name, salary
        FROM employees
        WHERE department_id = 10;
    
    -- 定义集合类型
    TYPE emp_id_array IS TABLE OF employees.employee_id%TYPE;
    TYPE name_array IS TABLE OF employees.last_name%TYPE;
    TYPE sal_array IS TABLE OF employees.salary%TYPE;
    
    v_ids emp_id_array;
    v_names name_array;
    v_sals sal_array;
BEGIN
    OPEN emp_cursor;
    
    -- 批量提取数据
    FETCH emp_cursor BULK COLLECT INTO v_ids, v_names, v_sals;
    
    CLOSE emp_cursor;
    
    -- 处理批量数据
    FOR i IN 1..v_ids.COUNT LOOP
        DBMS_OUTPUT.PUT_LINE(v_ids(i) || ': ' || v_names(i) || ', ' || v_sals(i));
    END LOOP;
END;
/

3. 批量处理与FORALL

DECLARE
    TYPE id_array IS TABLE OF employees.employee_id%TYPE;
    TYPE sal_array IS TABLE OF employees.salary%TYPE;
    
    v_ids id_array := id_array(101, 102, 103, 104, 105);
    v_new_sals sal_array;
BEGIN
    -- 批量查询
    SELECT salary BULK COLLECT INTO v_new_sals
    FROM employees
    WHERE employee_id IN (SELECT COLUMN_VALUE FROM TABLE(v_ids));
    
    -- 批量更新
    FORALL i IN 1..v_ids.COUNT
        UPDATE employees
        SET salary = v_new_sals(i) * 1.1
        WHERE employee_id = v_ids(i);
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('成功更新 ' || SQL%ROWCOUNT || ' 条记录');
END;
/

四、游标最佳实践

1.  及时关闭游标:避免资源泄漏

BEGIN
    OPEN emp_cursor;
    -- 处理数据
EXCEPTION
    WHEN OTHERS THEN
        IF emp_cursor%ISOPEN THEN
            CLOSE emp_cursor;
        END IF;
        RAISE;
END;

2. 使用游标FOR循环:简化代码,自动处理打开/关闭

3. 批量操作:对大结果集使用BULK COLLECT和FORALL

4. 参数化游标:提高代码重用性

5. 限制返回行数:避免内存问题

FETCH emp_cursor BULK COLLECT INTO v_emps LIMIT 1000;

6. 使用合适的作用域:在包中定义常用游标

7. 性能监控:检查游标SQL的执行计划

五、高级游标技术

1. 可更新游标 

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, last_name, salary
        FROM employees
        WHERE department_id = 10
        FOR UPDATE OF salary NOWAIT;
    
    v_raise_percent NUMBER := 0.1;
BEGIN
    FOR emp_rec IN emp_cursor LOOP
        UPDATE employees
        SET salary = salary * (1 + v_raise_percent)
        WHERE CURRENT OF emp_cursor;
    END LOOP;
    
    COMMIT;
END;
/

2. 游标子查询

DECLARE
    CURSOR dept_cursor IS
        SELECT d.department_id, d.department_name,
               CURSOR(
                   SELECT employee_id, last_name
                   FROM employees
                   WHERE department_id = d.department_id
               ) AS emp_cursor
        FROM departments d
        WHERE d.location_id = 1700;
    
    v_emp_cursor SYS_REFCURSOR;
    v_emp_id employees.employee_id%TYPE;
    v_emp_name employees.last_name%TYPE;
BEGIN
    FOR dept_rec IN dept_cursor LOOP
        DBMS_OUTPUT.PUT_LINE('部门: ' || dept_rec.department_name);
        
        v_emp_cursor := dept_rec.emp_cursor;
        LOOP
            FETCH v_emp_cursor INTO v_emp_id, v_emp_name;
            EXIT WHEN v_emp_cursor%NOTFOUND;
            DBMS_OUTPUT.PUT_LINE('  ' || v_emp_id || ': ' || v_emp_name);
        END LOOP;
        CLOSE v_emp_cursor;
    END LOOP;
END;
/

3. 游标表达式(12c+)

DECLARE
    CURSOR dept_cursor IS
        SELECT d.department_id, 
               d.department_name,
               CURSOR(
                   SELECT e.employee_id, e.last_name
                   FROM employees e
                   WHERE e.department_id = d.department_id
               ) AS emp_cur
        FROM departments d;
BEGIN
    FOR dept_rec IN dept_cursor LOOP
        DBMS_OUTPUT.PUT_LINE('部门: ' || dept_rec.department_name);
        
        FOR emp_rec IN dept_rec.emp_cur LOOP
            DBMS_OUTPUT.PUT_LINE('  员工: ' || emp_rec.last_name);
        END LOOP;
    END LOOP;
END;
/

游标是Oracle PL/SQL中处理结果集的核心机制,掌握各种游标技术可以显著提高数据库应用程序的效率和灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2325441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

畅享电脑流畅运行:深度卸载、智能监视与空间释放

软件介绍 在数字化办公与娱乐高度融合的当下&#xff0c;电脑承载着我们诸多重要任务&#xff0c;然而&#xff0c;随着软件的频繁安装与卸载&#xff0c;系统逐渐被各种顽固软件及其残留 “拖垮”&#xff0c;运行速度变慢、磁盘空间告急等问题接踵而至。别愁&#xff0c;今天…

Visual Studio Code 无法打开源文件解决方法

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux &#x1f525; 系列专栏&#xff1a;C从入门到精通 目录 一&#xff1a;&#x1f525; 突发状况 二&#xff1a;&#x1f525; 共勉 一&#xff1a;&#x1f525; 突发状况 &#x1f42c;…

核函数(机器学习深度学习)

一、核函数的基本概念 核函数&#xff08;Kernel Function&#xff09; 是机器学习中处理非线性问题的核心工具&#xff0c;通过隐式映射将数据从原始空间转换到高维特征空间&#xff0c;从而在高维空间中实现线性可分或线性建模。其数学本质是计算两个样本在高维空间中的内积…

【工具】BioPred一个用于精准医疗中生物标志物分析的 R 软件包

介绍 R 语言包 BioPred 提供了一系列用于精准医疗中的亚组分析和生物标志物分析的工具。它借助极端梯度提升&#xff08;XGBoost&#xff09;算法&#xff0c;并结合倾向得分加权和 A 学习方法&#xff0c;帮助优化个体化治疗规则&#xff0c;从而简化亚组识别过程。BioPred 还…

【银河麒麟系统常识】命令:dotnet --list-sdks(列出已安装的 .NET SDK 版本)

命令&#xff1a; dotnet --list-sdks 功能 列出当前系统中所有已安装的 .NET SDK 版本&#xff1b; 返回值规则 # 1. 格式&#xff1a;<版本号>[<安装路径>]&#xff1b; # 2. 排序&#xff1a;按版本号从低到高排序&#xff1b;示例

关于JVM和OS中的栈帧的区别和内存浅析

关于JVM和OS中的栈帧的区别和内存浅析 刚看了黑马JVM中的栈帧的讲解&#xff0c;感觉和自己理解的栈帧有一定出入&#xff0c;查询资料研究了一下发现的确有天壤之别&#xff0c;可惜黑马并没有讲。 故写下这篇文章巩固一下, OS的栈帧&#xff1a; ​ OS的栈帧会在调用一个函…

拥抱健康生活,开启养生之旅

在快节奏的现代生活中&#xff0c;健康养生愈发重要。它不仅能让我们拥有强健体魄&#xff0c;还能提升生活质量。​ 均衡饮食是养生的基石。多吃蔬菜和水果&#xff0c;它们富含维生素与膳食纤维。比如西兰花&#xff0c;堪称 “蔬菜皇冠”&#xff0c;不仅含有丰富的维生素 …

visual studio 2017配置QT5.9.4环境

前提是已经安装完毕vs 2017以及QT5.9.4&#xff0c;然后再进行下列的操作 一 环境配置 修改成如下所示&#xff0c;然后关闭vs 打开浏览器&#xff0c;搜索网站download.qt.io 如果2.4.1版本出现问题&#xff0c;可以换版本&#xff0c;如2.3.1&#xff0c;2.7.1都比较稳定 …

基于EFISH-SBC-RK3576的无人机智能飞控与数据存储方案

一、方案背景 民用无人机在电力巡检、农业植保、应急救援等领域快速普及&#xff0c;但传统方案面临‌多协议设备兼容性差‌、‌野外环境数据易丢失‌、‌复杂电磁干扰‌三大痛点。 电鱼智能推出‌EFISH-SBC-RK3576‌&#xff0c;可集成双冗余总线接口与工业级加固存储&#x…

c++的特性——多态

目录 概念 多态实现条件 虚函数 虚函数的重写/覆盖 练习题 析构函数的重写 override和final关键字 重载/隐藏/重载的区别 纯虚函数和抽象类 多态 虚函数表指针 多态的原理 动态绑定与静态绑定 虚函数表总结 前面学习了C的三个特性中的两个特性&#xff0c;今天我们…

MySQL基础语法DDLDML

目录 #1.创建和删除数据库 ​#2.如果有lyt就删除,没有则创建一个新的lyt #3.切换到lyt数据库下 #4.创建数据表并设置列及其属性,name是关键词要用name包围 ​编辑 #5.删除数据表 #5.查看创建的student表 #6.向student表中添加数据,数据要与列名一一对应 #7.查询studen…

性能测试理论基础-性能指标及jmeter中的指标

1、什么是性能测试 通过一定的手段,在多并发下情况下,获取被测系统的各项性能指标,验证被测系统在高并发下的处理能力、响应能力,稳定性等,能否满足预期。定位性能瓶颈,排查性能隐患,保障系统的质量,提升用户体验。 2、什么样的系统需要做性能测试 用户量大,页面访问…

Postman CORS 测试完全指南:轻松模拟跨域请求,排查 CORS 相关问题

在使用 Postman 进行 API 测试时&#xff0c;通常不会遇到跨域问题&#xff0c;因为 Postman 是一个独立的客户端应用程序&#xff0c;不同于在浏览器中运行的 JavaScript 代码&#xff0c;它没有同源策略&#xff08;SOP&#xff09;的限制。跨域资源共享&#xff08;CORS&…

云原生系列-K8S实战

K8S实战 1. K8S 资源创建方式2. NameSpace 资源创建3. Pod4. Deployment5. Service6. Ingress7. 存储抽象1. 环境准备2. PV&PVC1&#xff09; 创建PV池2&#xff09; PVC创建与绑定 3. ConfigMap 抽取应用配置&#xff0c;并且可以自动更新1&#xff09; redis 示例2) 创建…

centos8上实现lvs集群负载均衡nat模式

1.背景&#xff1a; 个人&#xff08;菜鸟&#xff09;学习笔记&#xff0c;学点记下来&#xff0c;给未来的自己看。高手看了也请多指点。 按照课程讲&#xff0c;lvs是我国大神开发的负载均衡程序&#xff0c;被收录进内核&#xff0c;只要安装时内核里有它&#xff0c;它就…

影响HTTP网络请求的因素

影响 HTTP 网络请求的因素 1. 带宽 2. 延迟 浏览器阻塞&#xff1a;浏览器会因为一些原因阻塞请求&#xff0c;浏览器对于同一个域名&#xff0c;同时只能有4个连接&#xff08;这个根据浏览器内核不同可能会有所差异&#xff09;&#xff0c;超过浏览器最大连接数限制&…

(UI自动化测试web端)第二篇:元素定位的方法_css定位之css选择器

看代码里的【find_element_by_css_selector( )】( )里的表达式怎么写&#xff1f; 文章介绍了第三种写法css选择器&#xff0c;你要根据网页中的实际情况来判断自己到底要用哪一种方法来进行元素定位。每种方法都要多练习&#xff0c;全都熟了之后你在工作当中使用起来元素定位…

MPU6050模块详解:从原理到STM32驱动指南(上) | 零基础入门STM32第八十九步

主题内容教学目的/扩展视频加速度传感器电路连接。手册分析。驱动程序&#xff0c;读出数据。能读出3轴数据。 师从洋桃电子&#xff0c;杜洋老师 &#x1f4d1;文章目录 一、MPU6050模块介绍1.1 核心特性1.2 模块化优势 二、MPU6050模块连接方法2.1 硬件连接2.2 电源注意事项 …

STM32 MODBUS-RTU主从站库移植

代码地址 STM32MODBUSRTU: stm32上的modbus工程 从站 FreeModbus是一个开源的Modbus通信协议栈实现。它允许开发者在各种平台上轻松地实现Modbus通信功能&#xff0c;包括串口和以太网。FreeMODBUS提供了用于从设备和主站通信的功能&#xff0c;支持Modbus RTU和Modbus TCP协…

架构师面试(二十二):TCP 协议

问题 今天我们聊一个非常常见的面试题目&#xff0c;不管前端还是后端&#xff0c;也不管做的是上层业务还是底层框架&#xff0c;更不管技术方向是运维还是架构&#xff0c;都可以思考和参与一下哈&#xff01; TCP协议无处不在&#xff0c;我们知道 TCP 是基于连接的端到端…