sql cursor 游标 学习笔记

转自尚硅谷 http://www.atguigu.com   宋红康

游标的概念
在处理sql语句的时候,oracle必须分配一片名叫上下文(context area)的区域来处理所必须的信息,其中包括要处理的行的数目,被分析后语句的指针以及查询的活动集。
游标是一个指向context area的句柄(handle)或指针,它们可以被指针控制。
SQL语句 游标
非查询语句
隐式
 结果是单行的查询语句
隐式或显式
 结果是多行的查询语句
显式

显式游标
显式游标处理需四个PL/SQL步骤 :

定义游标
就是定义一个游标名,以及与其相对应的 SELECT 语句。

格式:  CURSOR cursor_name[(parameter[, parameter]…)] IS select_statement;


注意:
在指定数据类型时,不能使用长度约束 。如 NUMBER(4),CHAR(10) 等都是错误的。

打开游标:

格式:
OPEN
cursor_name[([parameter =>] value[, [parameter =>] value]…)];

在向游标传递参数时,可以使用与函数参数相同的传值方法,
即位置表示法和名称表示
程序不能用OPEN语句重复打开一个游标。


提取游标:就是检索结果集合中的数据行,放入指定的输出变量中。

格式:       

FETCH cursor_name INTO {variable_list | record_variable };


关闭游标:当提取和处理完游标结果集合数据后,应及时关闭游标,以释放该游标所占用的系统资源,

并使该游标的工作区变成无效,不能再使用 FETCH语句取其中数据。
关闭后的游标可以使用
OPEN语句重新打开。

格式:

CLOSE  cursor_name;

注:定义的游标不能有 INTO子句。


1.查询前10名员工的信息。




2.游标参数的传递方法。


或者

游标属性
%FOUND               布尔型属性,当最近一次读记录时成功返回,则值TRUE
%NOTFOUND    布尔型属性,与%FOUND相反;
%ISOPEN          布尔型属性,当游标已打开时返回TRUE;
%ROWCOUNT   数字型属性,返回已从游标中读取的记录数。

3:给工资低于 3000的员工工资调为3000


游标的 FOR  循环

FOR循环语句,自动执行游标的  OPENFETCHCLOSE语句和循环语句的功能;

当进入循环时,游标 FOR循环语句自动打开游标,并提取第一行游标数据,当程序处理完当前所提取的数

据而进入下一次循环时,游标 FOR循环语句自动提取下一行数据供程序处理,当提取完结果集合中的所有

数据行后结束循环,并自动关闭游标。


格式:

FOR index_variable IN cursor_name[value[, value]…]  LOOP

--游标数据处理代码

END LOOP;

其中,index_variable为游标  FOR循环语句隐含声明的索引变量,该变量为记录变量,其结构与游标查询语句

返回的结构集合的结构相同。在程序中可以通过引用该索引记录变量元素来读取所提取的游标数据,

index_variable中各元素的名称与游标查询语句选择列表中所制定的列名相同。如果在游标查询语句的选择

列表中存在计算列,则必须为这些计算列指定别名后才能通过游标 FOR循环语句中的索引变量来访问这些

列数据。

注:不要在程序中对游标进行人工操作;不要在程序中定义用于控制 FOR 循环的记录。

 4





5:当所声明的游标带有参数时,通过游标 FOR循环语句为游标传递参数。


或者


6PL/SQL还允许在游标   FOR循环语句中使用子查询来实现游标的功能。




隐式游标

显式游标主要是用于对查询语句的处理,尤其是在查询结果为多条记录的情况下;

而对于非查询语句,
如修改、删除操作,则由 ORACLE系统自动地为这些操作设置游标并创建其工作区,这些由系统隐含创建的游标称为隐式游标,隐式游标的名字为 SQL,这是由 ORACLE系统定义的。

对于隐式游标的操作,如定义、打开、取值及关闭操作,都由  ORACLE系统自动地完成,无需用户进行处理。用户只能通过隐式游标的相关属性,来完成相应的操作。在隐式游标的工作区中,所存放的数据是与用户自定义的显示游标无关的、

最新处理的一条 SQL语句所包含的数据。


格式调用为:
  SQL%

%FOUND               布尔型属性,当最近一次读记录时成功返回,则值TRUE
%NOTFOUND         布尔型属性,与%FOUND相反;
%ISOPEN               布尔型属性,取值总是FALSE。SQL命令执行完毕立即关闭隐式游标;
%ROWCOUNT        数字型属性,返回已从游标中读取的记录数。


7:更新指定员工信息,如果该员工没有找到,则打印查无此人信息。




NO_DATA_FOUND
和 %NOTFOUND的区别

SELECT … INTO语句触发NO_DATA_FOUND

当一个显式游标的WHERE子句未找到时触发%NOTFOUND;


UPDATE   DELETE语句的 WHERE子句未找到时触发 SQL%NOTFOUND

在提取循环中要用到%NOTFOUND%FOUND来确定循环的退出条件不要用NO_DATA_FOUND。


游标的修改和操作

游标修改和删除操作是指在游标定位下,修改或删除表中指定的数据行。这时,要求游标查询语句中

必须使用 FOR UPDATE选项,以便在打开游标时锁定游标结果集合在表中对应数据行的所有列和部分列。

为了对正在处理(查询)的行不被另外的用户改动,ORACLE提供一个     FOR UPDATE子句来对所选择的行

进行锁住。该需求迫使 ORACLE锁定游标结果集合的行,可以防止其他事务处理更新或删除相同的行,直到

您的事务处理提交或回退为止。


语法:

SELECT . . . FROM … FOR UPDATE [OF column[, column]…] [NOWAIT

如果另一个会话已对活动集中的行加了锁,那么 SELECT FOR UPDATE操作一直等待到其它的会话释放这

些锁后才继续自己的操作,对于这种情况,当加上  NOWAIT子句时,如果这些行真的被另一个会话锁定,

OPEN立即返回并给出:

ora-0054:resource busy and acquire with nowait specified。


如果使用
    FOR  UPDATE声明游标,则可在        DELETE     UPDATE语句中使用       WHERE  CURRENT   OF

cursor_name子句,修改或删除游标结果集合当前行对应的数据库表中的数据行。


8:从 EMPLOYEES表中查询某部门的员工情况,将其工资最低定为3000




以下为尚硅谷的讲解习题。
http://www.atguigu.com   宋红康
*********************************************************************************************************
12.1 使用游标

要求: 打印出 80 部门的所有的员工的工资:salary: xxx
 
declare
  --1. 定义游标
  cursor salary_cursor is select salary from employees where department_id = 80;
  v_salary employees.salary%type;
begin
 --2. 打开游标
 open salary_cursor;
--3. 提取游标
 fetch salary_cursor into v_salary;
--4. 对游标进行循环操作: 判断游标中是否有下一条记录
while salary_cursor%found loop
      dbms_output.put_line('salary: ' || v_salary);
      fetch salary_cursor into v_salary;
end loop;  
 --5. 关闭游标
 close  salary_cursor;
end;


12.2 使用游标
要求: 打印出 80 部门的所有的员工的工资: Xxx 's salary is: xxx
 declare
  cursor sal_cursor is select salary ,last_name from employees where department_id = 80;
  v_sal number(10);
  v_name varchar2(20);
begin
  open sal_cursor;
  
  fetch sal_cursor into v_sal,v_name;
  
  while sal_cursor%found loop
        dbms_output.put_line(v_name||'`s salary is '||v_sal);
        fetch sal_cursor into v_sal,v_name;
  end loop;
  
  close sal_cursor;
  
end;


13. 使用游标的练习: 
打印出 manager_id 为 100 的员工的 last_name, email, salary 信息(使用游标, 记录类型)

declare  
           --声明游标    
           cursor emp_cursor is select last_name, email, salary from employees where manager_id = 100;
           
           --声明记录类型
           type emp_record is record(
                name employees.last_name%type,
                email employees.email%type,
                salary employees.salary%type
           );
           
           -- 声明记录类型的变量
           v_emp_record emp_record;
begin
           --打开游标
           open emp_cursor;
           
           --提取游标
           fetch emp_cursor into v_emp_record;
           
           --对游标进行循环操作
           while emp_cursor%found loop
                  dbms_output.put_line(v_emp_record.name || ', ' || v_emp_record.email || ', ' || v_emp_record.salary );                
                  fetch emp_cursor into v_emp_record;
           end loop;
           
           --关闭游标
           close emp_cursor;
end;
(法二:使用for循环)
declare
   
      cursor emp_cursor is 
      select last_name,email,salary
      from employees
      where manager_id = 100;


begin
      for v_emp_record in emp_cursor loop
          dbms_output.put_line(v_emp_record.last_name||','||v_emp_record.email||','||v_emp_record.salary);
      end loop;
end;


14. 利用游标, 调整公司中员工的工资: 
    
    工资范围       调整基数
    0 - 5000       5%
    5000 - 10000   3%
    10000 - 15000  2%
    15000 -        1%


declare
    --定义游标
    cursor emp_sal_cursor is select salary, employee_id from employees;
    
    --定义基数变量
    temp number(4, 2);
    
    --定义存放游标值的变量
    v_sal employees.salary%type;
    v_id employees.employee_id%type;
begin
    --打开游标
    open emp_sal_cursor;
    
    --提取游标
    fetch emp_sal_cursor into v_sal, v_id;
    
    --处理游标的循环操作
    while emp_sal_cursor%found loop
          --判断员工的工资, 执行 update 操作
          --dbms_output.put_line(v_id || ': ' || v_sal);
            
          if v_sal <= 5000 then
             temp := 0.05;
          elsif v_sal<= 10000 then
             temp := 0.03;   
          elsif v_sal <= 15000 then
             temp := 0.02;
          else
             temp := 0.01;
          end if;
          
          --dbms_output.put_line(v_id || ': ' || v_sal || ', ' || temp);
          update employees set salary = salary * (1 + temp) where employee_id = v_id; 
                  
          fetch emp_sal_cursor into v_sal, v_id;
    end loop;
    --关闭游标
    close emp_sal_cursor;
end;


使用SQL中的 decode 函数
update employees set salary = salary * (1 + (decode(trunc(salary/5000), 0, 0.05,
                                                                        1, 0.03,
                                                                        2, 0.02,
                                                                        0.01)))


15. 利用游标 for 循环完成 14. 

declare
    --定义游标
    cursor emp_sal_cursor is select salary, employee_id id from employees;
    
    --定义基数变量
    temp number(4, 2);
begin
    --处理游标的循环操作
    for c in emp_sal_cursor loop
          --判断员工的工资, 执行 update 操作
          --dbms_output.put_line(v_id || ': ' || v_sal);
            
          if c.salary <= 5000 then
             temp := 0.05;
          elsif c.salary <= 10000 then
             temp := 0.03;   
          elsif c.salary <= 15000 then
             temp := 0.02;
          else
             temp := 0.01;
          end if;
          
          --dbms_output.put_line(v_id || ': ' || v_sal || ', ' || temp);
          update employees set salary = salary * (1 + temp) where employee_id = c.id;
    end loop;
end;


16*. 带参数的游标


declare
    --定义游标
    cursor emp_sal_cursor(dept_id number, sal number) is 
           select salary + 1000 sal, employee_id id 
           from employees 
           where department_id = dept_id and salary > sal;
    
    --定义基数变量
    temp number(4, 2);
begin
    --处理游标的循环操作
    for c in emp_sal_cursor(sal => 4000, dept_id => 80) loop
          --判断员工的工资, 执行 update 操作
          --dbms_output.put_line(c.id || ': ' || c.sal);
          
          if c.sal <= 5000 then
             temp := 0.05;
          elsif c.sal <= 10000 then
             temp := 0.03;   
          elsif c.sal <= 15000 then
             temp := 0.02;
          else
             temp := 0.01;
          end if;
          
          dbms_output.put_line(c.sal || ': ' || c.id || ', ' || temp);
          --update employees set salary = salary * (1 + temp) where employee_id = c.id;
    end loop;
end;


17. 隐式游标: 更新指定员工 salary(涨工资 10),如果该员工没有找到,则打印”查无此人” 信息
begin
         update employees set salary = salary + 10 where employee_id = 1005;
         
         if sql%notfound then
            dbms_output.put_line('查无此人!');
         end if;
end;
请使用浏览器的分享功能分享到微信等