您好!欢迎访问家园网-www.jy.wang!

家园网

游标参数调用覆盖的机制

网络 作者:本站 点击:

下面是Oracle数据库游标使用的一段代码,看不大明白,

讲解一下。怎么前面是‘SALEMAN’,后面又成了‘MANAGER’了?


set serveroutput on
DECLARE
  /*声明游标,查询员工信息*/
  CURSOR cur_emp (var_job in varchar2:='SALESMAN')
  IS select empno,ename,sal
     from emp
     where job=var_job;
  type record_emp is record    --声明一个记录类型(RECORD类型)
  (
    /*定义当前记录的成员变量*/
    var_empno emp.empno%type,
    var_ename emp.ename%type,
    var_sal emp.sal%type
  );
  emp_row record_emp;          --声明一个record_emp类型的变量
BEGIN
  OPEN cur_emp('MANAGER');     --打开游标
  FETCH cur_emp INTO emp_row;  --先让指针指向结果集中的第一行,并将值保存到emp_row中
  WHILE cur_emp%found LOOP
    dbms_output.put_line(emp_row.var_ename||'的编号是'||emp_row.var_empno||',工资是'||emp_row.var_sal);
    FETCH cur_emp INTO emp_row;	--让指针指向结果集中的下一行,并将值保存到emp_row中
 END LOOP;
  CLOSE cur_emp;                --关闭游标
END;
/

Oracle 游标代码逐行详解

你观察得非常仔细!'SALESMAN' 和 'MANAGER' 这个对比正是理解游标参数机制的关键。让我从头讲清楚。


一、核心问题:为什么前面是 'SALESMAN',后面是 'MANAGER'

位置

代码

含义

游标声明

var_job in varchar2:='SALESMAN'

默认值:如果不传参数,就用 'SALESMAN'

打开游标

OPEN cur_emp('MANAGER')

实际传参:这里传入了 'MANAGER',覆盖默认值


一句话:默认值是 'SALESMAN',但调用时传了 'MANAGER',所以实际查询的是经理,不是销售员。


二、逐行详细讲解

第1行:set serveroutput on

sql

set serveroutput on

作用:允许输出 dbms_output 的内容到屏幕。没有这行,你看不到任何输出。


第2行:DECLARE

sql

DECLARE

作用:PL/SQL 块的声明部分开始。这里要声明游标、类型、变量。


第3-7行:声明游标(带参数和默认值)

sql

  /*声明游标,查询员工信息*/
  CURSOR cur_emp (var_job in varchar2:='SALESMAN')
  IS select empno,ename,sal     from emp     where job=var_job;

这部分可以拆解为:

部分含义
CURSOR cur_emp声明一个名叫 cur_emp 的游标
(var_job in varchar2:='SALESMAN')游标有一个输入参数 var_job,类型是字符串,默认值是 'SALESMAN'
IS SELECT ... WHERE job = var_job游标执行的查询,用参数作为过滤条件

关键理解

  • 参数 var_job 的值可以在打开游标时指定

  • 如果不指定,就用默认值 'SALESMAN'


第8-12行:声明 RECORD 类型

sql

  type record_emp is record    --声明一个记录类型(RECORD类型)
  (
    /*定义当前记录的成员变量*/
    var_empno emp.empno%type,
    var_ename emp.ename%type,
    var_sal emp.sal%type
  );

部分含义
type record_emp is record声明一个名叫 record_emp 的记录类型
var_empno emp.empno%type第一个字段,类型和 emp.empno 一样
var_ename emp.ename%type第二个字段,类型和 emp.ename 一样
var_sal emp.sal%type第三个字段,类型和 emp.sal 一样

%type 的含义:取某列的数据类型。比如 emp.empno%type 就是 emp 表中 empno 列的数据类型(NUMBER)。


第13行:声明 RECORD 变量

sql

  emp_row record_emp;          --声明一个record_emp类型的变量

作用:创建一个 record_emp 类型的变量,名叫 emp_row。这个变量可以存放一行数据(三个字段)。


第14行:BEGIN

sql

BEGIN

作用:PL/SQL 块的执行部分开始。


第15行:打开游标(关键!

sql

  OPEN cur_emp('MANAGER');     --打开游标

这是理解你疑问的关键行

写法实际使用的参数查询结果
OPEN cur_emp使用默认值 'SALESMAN'查销售员
OPEN cur_emp('MANAGER')使用传入值 'MANAGER'查经理
OPEN cur_emp('CLERK')使用传入值 'CLERK'查办事员

所以:虽然游标声明时默认是 'SALESMAN',但这里传入了 'MANAGER'覆盖了默认值,实际查询的是经理,不是销售员。


第16行:第一次取数据

sql

  FETCH cur_emp INTO emp_row;  --先让指针指向结果集中的第一行,并将值保存到emp_row中

作用

  • 从游标中取第一行数据

  • 把这一行数据放入 emp_row 变量中

  • 游标指针下移

访问方式emp_row.var_empnoemp_row.var_enameemp_row.var_sal


第17行:WHILE cur_emp%found LOOP

sql

  WHILE cur_emp%found LOOP

部分含义
WHILE当...的时候
cur_emp%found游标属性,判断上一次 FETCH 是否取到了数据
LOOP循环开始

逻辑:只要上一次 FETCH 成功取到了数据,就继续循环。


第18行:输出当前行的数据

sql

    dbms_output.put_line(emp_row.var_ename||'的编号是'||emp_row.var_empno||',工资是'||emp_row.var_sal);

作用:打印当前员工的信息。


第19行:取下一行数据

sql

    FETCH cur_emp INTO emp_row;	--让指针指向结果集中的下一行,并将值保存到emp_row中

作用:取下一行数据,覆盖 emp_row 中的旧值。这是循环能继续的关键。


第20行:END LOOP;

sql

 END LOOP;

作用:循环结束,跳回 WHILE 继续判断。


第21行:关闭游标

sql

  CLOSE cur_emp;                --关闭游标

作用:释放游标占用的资源。游标关闭后不能再 FETCH。


第22-23行:结束

sql

END;/

作用:PL/SQL 块结束,/ 执行这个块。


三、执行流程图

360截图20260502194708477.jpg

四、%FOUND 和 %NOTFOUND 对比

代码位置

%FOUND 值

%NOTFOUND 值

说明

OPEN 之后、第一次 FETCH 之前

NULL

NULL

还没取过数据

第一次 FETCH 成功(有数据)

TRUE

FALSE

取到一行

中间 FETCH 成功

TRUE

FALSE

取到一行

最后一次 FETCH 失败(没数据了)

FALSE

TRUE

循环应该退出

五、为什么这个 RECORD 类型要手动定义?

方式

代码

优缺点

手动定义

type record_emp is record (...)

可以自定义字段名(如 var_ename

自动定义

emp_row cur_emp%ROWTYPE

更简洁,字段名就是原表列名


这个代码之所以手动定义,可能是因为:

  • 想把字段名改成 var_xxx 的命名风格

  • 或者教学演示 RECORD 类型的定义语法

更常见的简洁写法

DECLARE
  CURSOR cur_emp(var_job IN VARCHAR2 := 'SALESMAN') IS
    SELECT empno, ename, sal FROM emp WHERE job = var_job;
  emp_row cur_emp%ROWTYPE;  -- 不需要手动定义 RECORD
BEGIN
  OPEN cur_emp('MANAGER');
  LOOP
    FETCH cur_emp INTO emp_row;
    EXIT WHEN cur_emp%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(emp_row.ename || '的编号是' || emp_row.empno || ',工资是' || emp_row.sal);
  END LOOP;
  CLOSE cur_emp;
END;
/


六、总结

问题答案
为什么有 'SALESMAN'游标声明的默认值——如果不传参,就查销售员
为什么又成了 'MANAGER'打开游标时传入了 'MANAGER'覆盖了默认值
实际查的是什么?经理(job = 'MANAGER'
RECORD 类型是什么?一种自定义的复合类型,可以包含多个字段
为什么要用 RECORD?一次 FETCH 就能接收整行数据,不用声明多个变量

一句话总结:游标声明时的 'SALESMAN' 只是默认值,打开游标时传入的 'MANAGER' 会覆盖它,所以实际查询的是经理。这就是游标参数的灵活之处——同一个游标可以查不同职位,只需在 OPEN 时传入不同的参数值


标签: