查看: 2436|回复: 0

[Oracle数据库] Oracle中游标Cursor基本用法详解

发表于 2018-3-28 08:00:06

查询

  SELECT语句用于从数据库中查询数据,当在PL/SQL中使用SELECT语句时,要与INTO子句一起使用,查询的
返回值被赋予INTO子句中的变量,变量的声明是在DELCARE中。SELECT INTO语法如下:

  1.   SELECT [DISTICT|ALL]{*|column[,column,...]}
  2.   INTO (variable[,variable,...] |record)
  3.   FROM {table|(sub-query)}[alias]
  4.   WHERE............
复制代码

  PL/SQL中SELECT语句只返回一行数据。如果超过一行数据,那么就要使用显式游标(对游标的讨论我们将
在后面进行),INTO子句中要有与SELECT子句中相同列数量的变量。INTO子句中也可以是记录变量。

%TYPE属性

  在PL/SQL中可以将变量和常量声明为内建或用户定义的数据类型,以引用一个列名,同时继承他的数据类
型和大小。这种动态赋值方法是非常有用的,比如变量引用的列的数据类型和大小改变了,如果使用了%TYPE,
那么用户就不必修改代码,否则就必须修改代码。

例:

  1.   v_empno SCOTT.EMP.EMPNO%TYPE;
  2.   v_salary EMP.SALARY%TYPE;
复制代码

  不但列名可以使用%TYPE,而且变量、游标、记录,或声明的常量都可以使用%TYPE。这对于定义相同数据类
型的变量非常有用。

  1.   DELCARE
  2. V_A NUMBER(5):=10;
  3.   V_B V_A%TYPE:=15;
  4.   V_C V_A%TYPE;
  5.   BEGIN
  6.   DBMS_OUTPUT.PUT_LINE
  7.   ('V_A='||V_A||'V_B='||V_B||'V_C='||V_C);
  8.   END
  9.   SQL>/
  10.   V_A=10 V_B=15 V_C=
  11.   PL/SQL procedure successfully completed.
  12.   SQL>
复制代码

其他DML语句

其它操作数据的DML语句是:INSERT、UPDATE、DELETE和LOCK TABLE,这些语句在PL/SQL中的语法与在SQL中
的语法相同。我们在前面已经讨

论过DML语句的使用这里就不再重复了。在DML语句中可以使用任何在DECLARE部分声明的变量,如果是嵌套
块,那么要注意变量的作用范围。

例:

  1.   CREATE OR REPLACE PROCEDURE FIRE_EMPLOYEE (pempno in number)
  2.   AS
  3.   v_ename EMP.ENAME%TYPE;
  4.   BEGIN
  5.   SELECT ename INTO v_ename
  6.   FROM emp
  7.   WHERE empno=p_empno;
  8.   INSERT INTO FORMER_EMP(EMPNO,ENAME)
  9.   VALUES (p_empno,v_ename);
  10.   DELETE FROM emp
  11.   WHERE empno=p_empno;
  12.   UPDATE former_emp
  13.   SET date_deleted=SYSDATE
  14.   WHERE empno=p_empno;
  15.   EXCEPTION
  16.   WHEN NO_DATA_FOUND THEN
  17.   DBMS_OUTPUT.PUT_LINE('Employee Number Not Found!');
  18.   END
复制代码

  DML语句的结果

当执行一条DML语句后,DML语句的结果保存在四个游标属性中,这些属性用于控制程序流程或者了解程序
的状态。当运行DML语句时,PL/SQL打开一个内建游标并处理结果,游标是维护查询结果的内存中的一个区域,
游标在运行DML语句时打开,完成后关闭。隐式游标只使用SQL%FOUND,

SQL%NOTFOUND,SQL%ROWCOUNT三个属性.SQL%FOUND,SQL%NOTFOUND是布尔值,SQL%ROWCOUNT是整数值。

SQL%FOUND和SQL%NOTFOUND

在执行任何DML语句前SQL%FOUND和SQL%NOTFOUND的值都是NULL,在执行DML语句后,SQL%FOUND的属性值将是:

  . TRUE :INSERT
  . TRUE :DELETE和UPDATE,至少有一行被DELETE或UPDATE.
  . TRUE :SELECT INTO至少返回一行

当SQL%FOUND为TRUE时,SQL%NOTFOUND为FALSE。

SQL%ROWCOUNT

在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL,对于SELECT INTO语句,如果执行成功
,SQL%ROWCOUNT的值为1,如果没有成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND.

SQL%ISOPEN

SQL%ISOPEN是一个布尔值,如果游标打开,则为TRUE, 如果游标关闭,则为FALSE.对于隐式游标而言SQL%
ISOPEN总是FALSE,这是因为隐式游

标在DML语句执行时打开,结束时就立即关闭。

事务控制语句

事务是一个工作的逻辑单元可以包括一个或多个DML语句,事物控制帮助用户保证数据的一致性。如果事务控制逻辑单元中的任何一个DML
语句失败,那么整个事务都将回滚,在PL/SQL中用户可以明确地使用COMMIT、ROLLBACK、SAVEPOINT以及
SET TRANSACTION语句。

COMMIT语句终止事务,永久保存数据库的变化,同时释放所有LOCK,ROLLBACK终止现行事务释放所有LOCK,
但不保存数据库的任何变化,SAVEPOI

NT用于设置中间点,当事务调用过多的数据库操作时,中间点是非常有用的,SET TRANSACTION用于设置事
务属性,比如read-write和隔离级等。

显式游标

查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用select into语句。PL/SQL管理隐式
游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。显式游标在PL/SQL块的声明部分声明,在执行部分或异常处理部分打开,取数据,关闭。

使用游标

这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的
游标都是指显式游标。要在程序中使用游标,必须首先声明游标。

声明游标

语法:

CURSOR cursor_name IS select_statement;

在PL/SQL中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。

例:

  1.   DELCARE
  2.   CURSOR C_EMP IS SELECT empno,ename,salary
  3.   FROM emp
  4.   WHERE salary>2000
  5.   ORDER BY ename;
  6.   ........
  7.   BEGIN
复制代码

  在游标定义中SELECT语句中不一定非要表可以是视图,也可以从多个表或视图中选择的列,甚至可以使用*
来选择所有的列 。

打开游标

使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法是:

  1.   OPEN cursor_name
复制代码

 cursor_name是在声明部分定义的游标名。

例:

  1. OPEN C_EMP;
复制代码

  关闭游标

语法:

  1.  CLOSE cursor_name
复制代码

例:

  1. CLOSE C_EMP;
复制代码

从游标提取数据

从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。语法如下:

FETCH cursor_name INTO variable[,variable,...]

对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变量的类型也要相同。

例:

  1.   SET SERVERIUTPUT ON
  2.   DECLARE
  3.   v_ename EMP.ENAME%TYPE;
  4.   v_salary EMP.SALARY%TYPE;
  5.   CURSOR c_emp IS SELECT ename,salary FROM emp;
  6.   BEGIN
  7.   OPEN c_emp;
  8.   FETCH c_emp INTO v_ename,v_salary;
  9. DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  10.   FETCH c_emp INTO v_ename,v_salary;
  11.   DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  12.   FETCH c_emp INTO v_ename,v_salary;
  13.   DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  14.   CLOSE c_emp;
  15.   END
复制代码

这段代码无疑是非常麻烦的,如果有多行返回结果,可以使用循环并用游标属性为结束循环的条件,以这
种方式提取数据,程序的可读性和简洁性都大为提高,下面我们使用循环重新写上面的程序:

  1.   SET SERVERIUTPUT ON
  2.   DECLARE
  3.   v_ename EMP.ENAME%TYPE;
  4.   v_salary EMP.SALARY%TYPE;
  5.   CURSOR c_emp IS SELECT ename,salary FROM emp;
  6.   BEGIN
  7.   OPEN c_emp;
  8.   LOOP
  9.   FETCH c_emp INTO v_ename,v_salary;
  10.   EXIT WHEN c_emp%NOTFOUND;
  11.   DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  12.   END
复制代码

  记录变量

定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。

记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方
便得多。

当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句
中使用*比将所有列名列出来要安全得多。

例:

  1.   SET SERVERIUTPUT ON
  2.   DECLARE
  3.   R_emp EMP%ROWTYPE;
  4.   CURSOR c_emp IS SELECT * FROM emp;
  5.   BEGIN
  6.   OPEN c_emp;
  7.   LOOP
  8.   FETCH c_emp INTO r_emp;
  9.   EXIT WHEN c_emp%NOTFOUND;
  10.   DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary);
  11.   END LOOP;
  12.   CLOSE c_emp;
  13.   END;
复制代码

%ROWTYPE也可以用游标名来定义,这样的话就必须要首先声明游标:

  1.   SET SERVERIUTPUT ON
  2.   DECLARE
  3.   CURSOR c_emp IS SELECT ename,salary FROM emp;
  4.   R_emp c_emp%ROWTYPE;
  5.   BEGIN
  6.   OPEN c_emp;
  7.   LOOP
  8.   FETCH c_emp INTO r_emp;
  9.   EXIT WHEN c_emp%NOTFOUND;
  10.   DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary);
  11.   END LOOP;
  12.   CLOSE c_emp;
  13.   END;
复制代码

带参数的游标

与存储过程和函数相似,可以将参数传递给游标并在查询中使用。这对于处理在某种条件下打开游标的情
况非常有用。它的语法如下:

CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;

定义参数的语法如下:

Parameter_name [IN] data_type[{:=|DEFAULT} value]

与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。

另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。

在打开游标时给参数赋值,语法如下:

OPEN cursor_name[value[,value]....];

参数值可以是文字或变量。

例:

  1.   DECALRE
  2.   CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno;
  3.   CURSOR c_emp (p_dept VARACHAR2) IS
  4.   SELECT ename,salary
  5.   FROM emp
  6.   WHERE deptno=p_dept
  7.   ORDER BY ename
  8.   r_dept DEPT%ROWTYPE;
  9.   v_ename EMP.ENAME%TYPE;
  10.   v_salary EMP.SALARY%TYPE;
  11.   v_tot_salary EMP.SALARY%TYPE;
  12.   BEGIN
  13.   OPEN c_dept;
  14.   LOOP
  15.   FETCH c_dept INTO r_dept;
  16.   EXIT WHEN c_dept%NOTFOUND;
  17.   DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
  18.   v_tot_salary:=0;
  19.   OPEN c_emp(r_dept.deptno);
  20.   LOOP
  21.   FETCH c_emp INTO v_ename,v_salary;
  22.   EXIT WHEN c_emp%NOTFOUND;
  23.   DBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary);
  24.   v_tot_salary:=v_tot_salary+v_salary;
  25.   END LOOP;
  26.   CLOSE c_emp;
  27.   DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
  28.   END LOOP;
  29.   CLOSE c_dept;
  30.   END;
复制代码

  游标FOR循环

在大多数时候我们在设计程序的时候都遵循下面的步骤:

1、打开游标
2、开始循环
3、从游标中取值
4、检查那一行被返回
5、处理
6、关闭循环
7、关闭游标

可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于
FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。

游标FOR循环的语法如下:

  1.   FOR record_name IN
  2.   (corsor_name[(parameter[,parameter]...)]
  3.   | (query_difinition)
  4.   LOOP
  5.   statements
  6.   END LOOP;
复制代码

  下面我们用for循环重写上面的例子:

  1.   DECALRE
  2.   CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno;
  3.   CURSOR c_emp (p_dept VARACHAR2) IS
  4.   SELECT ename,salary
  5.   FROM emp
  6.   WHERE deptno=p_dept
  7.   ORDER BY ename
  8.   v_tot_salary EMP.SALARY%TYPE;
  9. BEGIN
  10.   FOR r_dept IN c_dept LOOP
  11.   DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
  12.   v_tot_salary:=0;
  13.   FOR r_emp IN c_emp(r_dept.deptno) LOOP
  14.   DBMS_OUTPUT.PUT_LINE('Name:' || v_ename || 'salary:' || v_salary);
  15.   v_tot_salary:=v_tot_salary+v_salary;
  16.   END LOOP;
  17.   DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
  18.   END LOOP;
  19.   END;
复制代码

  在游标FOR循环中使用查询

在游标FOR循环中可以定义查询,由于没有显式声明所以游标没有名字,记录名通过游标查询来定义。

  1.   DECALRE
  2.   v_tot_salary EMP.SALARY%TYPE;
  3.   BEGIN
  4.   FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP
  5.   DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
  6.   v_tot_salary:=0;
  7.   FOR r_emp IN (SELECT ename,salary
  8.   FROM emp
  9.   WHERE deptno=p_dept
  10.   ORDER BY ename) LOOP
  11.   DBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary);
  12.   v_tot_salary:=v_tot_salary+v_salary;
  13.   END LOOP;
  14.   DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
  15.   END LOOP;
  16.   END;
复制代码

  游标中的子查询

  语法如下:

  1.   CURSOR C1 IS SELECT * FROM emp
  2.   WHERE deptno NOT IN (SELECT deptno
  3.   FROM dept
  4.   WHERE dname!='ACCOUNTING');
复制代码

  可以看出与SQL中的子查询没有什么区别。

  游标中的更新和删除

  在PL/SQL中依然可以使用UPDATE和DELETE语句更新或删除数据行。显式游标只有在需要获得多行数据的情
况下使用。PL/SQL提供了仅仅使  用游标就可以执行删除或更新记录的方法。
  UPDATE或DELETE语句中的WHERE CURRENT OF子串专门处理要执行UPDATE或DELETE操作的表中取出的最近的
数据。要使用这个方法,在声明游标  时必须使用FOR UPDATE子串,当对话使用FOR UPDATE子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其
  他对象只能查询这些数据行,不能进行UPDATE、DELETE或SELECT...FOR UPDATE操作。

语法:

  FOR UPDATE [OF [schema.]table.column[,[schema.]table.column]..
  [nowait]

  在多表查询中,使用OF子句来锁定特定的表,如果忽略了OF子句,那么所有表中选择的数据行都将被锁定。
如果这些数据行已经被其他会话锁定,那么正常情况下ORACLE将等待,直到数据行解锁。

  在UPDATE和DELETE中使用WHERE CURRENT OF子串的语法如下:

  WHERE{CURRENT OF cursor_name|search_condition}

  例:

  1.   DELCARE
  2.   CURSOR c1 IS SELECT empno,salary
  3.   FROM emp
  4.   WHERE comm IS NULL
  5.   FOR UPDATE OF comm;
  6.   v_comm NUMBER(10,2);
  7.   BEGIN
  8.   FOR r1 IN c1 LOOP
  9.   IF r1.salary<500 THEN
  10.   v_comm:=r1.salary*0.25;
  11.   ELSEIF r1.salary<1000 THEN
  12.   v_comm:=r1.salary*0.20;
  13.   ELSEIF r1.salary<3000 THEN
  14.   v_comm:=r1.salary*0.15;
  15.   ELSE
  16.   v_comm:=r1.salary*0.12;
  17.   END IF;
  18.   UPDATE emp;
  19.   SET comm=v_comm
  20.   WHERE CURRENT OF c1l;
  21.   END LOOP;
  22.   END
复制代码
  1. -声明游标
  2. --宗地表的调查日期LANDINFO_RESEARCHDATE
  3. --复制到流程表的权属调查时间FLOW_REASEARCHTIME
  4. DECLARE
  5. cursor cur_sel_all is select LANDINFO_RESEARCHDATE,LANDINFO_LANDNO from t_leoa_landinfo; --定义游标
  6. l_date t_leoa_landinfo.landinfo_researchdate%type; --声明变量分别保存t_leoa_landinfo的各列
  7. l_landNo t_leoa_landinfo.landinfo_landno%type;
  8. begin
  9. open cur_sel_all;
  10. loop --循环取数,并将游标数据填充到返回纪录集合中
  11. fetch cur_sel_all into l_date,l_landNo;
  12. exit when cur_sel_all%NOTFOUND; --循环退出条件
  13. if cur_sel_all%FOUND then --获取数据
  14. update T_LEOA_BOOKFLOW t2 set FLOW_REASEARCHTIME = l_date where l_landNo = t2.landinfo_landno;
  15. end if;
  16. end loop;
  17. close cur_sel_all;
  18. end;
复制代码

下面再分享一下另外一则游标使用方法的代码,具体如下:

  1. -- 声明游标;CURSOR cursor_name IS select_statement
  2. --For 循环游标
  3. --(1)定义游标
  4. --(2)定义游标变量
  5. --(3)使用for循环来使用这个游标
  6. declare
  7. --类型定义
  8. cursor c_job
  9. is
  10. select empno,ename,job,sal
  11. from emp
  12. where job='MANAGER';
  13. --定义一个游标变量v_cinfo c_emp%ROWTYPE ,该类型为游标c_emp中的一行数据类型
  14. c_row c_job%rowtype;
  15. begin
  16. for c_row in c_job loop
  17. dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);
  18. end loop;
  19. end;
  20. --Fetch游标
  21. --使用的时候必须要明确的打开和关闭
  22. declare
  23. --类型定义
  24. cursor c_job
  25. is
  26. select empno,ename,job,sal
  27. from emp
  28. where job='MANAGER';
  29. --定义一个游标变量
  30. c_row c_job%rowtype;
  31. begin
  32. open c_job;
  33. loop
  34. --提取一行数据到c_row
  35. fetch c_job into c_row;
  36. --判读是否提取到值,没取到值就退出
  37. --取到值c_job%notfound 是false
  38. --取不到值c_job%notfound 是true
  39. exit when c_job%notfound;
  40. dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);
  41. end loop;
  42. --关闭游标
  43. close c_job;
  44. end;
  45. --1:任意执行一个update操作,用隐式游标sql的属性%found,%notfound,%rowcount,%isopen观察update语句的执行情况。
  46. begin
  47. update emp set ENAME='ALEARK' WHERE EMPNO=7469;
  48. if sql%isopen then
  49. dbms_output.put_line('Openging');
  50. else
  51. dbms_output.put_line('closing');
  52. end if;
  53. if sql%found then
  54. dbms_output.put_line('游标指向了有效行');--判断游标是否指向有效行
  55. else
  56. dbms_output.put_line('Sorry');
  57. end if;
  58. if sql%notfound then
  59. dbms_output.put_line('Also Sorry');
  60. else
  61. dbms_output.put_line('Haha');
  62. end if;
  63. dbms_output.put_line(sql%rowcount);
  64. exception
  65. when no_data_found then
  66. dbms_output.put_line('Sorry No data');
  67. when too_many_rows then
  68. dbms_output.put_line('Too Many rows');
  69. end;
  70. declare
  71. empNumber emp.EMPNO%TYPE;
  72. empName emp.ENAME%TYPE;
  73. begin
  74. if sql%isopen then
  75. dbms_output.put_line('Cursor is opinging');
  76. else
  77. dbms_output.put_line('Cursor is Close');
  78. end if;
  79. if sql%notfound then
  80. dbms_output.put_line('No Value');
  81. else
  82. dbms_output.put_line(empNumber);
  83. end if;
  84. dbms_output.put_line(sql%rowcount);
  85. dbms_output.put_line('-------------');
  86. select EMPNO,ENAME into empNumber,empName from emp where EMPNO=7499;
  87. dbms_output.put_line(sql%rowcount);
  88. if sql%isopen then
  89. dbms_output.put_line('Cursor is opinging');
  90. else
  91. dbms_output.put_line('Cursor is Closing');
  92. end if;
  93. if sql%notfound then
  94. dbms_output.put_line('No Value');
  95. else
  96. dbms_output.put_line(empNumber);
  97. end if;
  98. exception
  99. when no_data_found then
  100. dbms_output.put_line('No Value');
  101. when too_many_rows then
  102. dbms_output.put_line('too many rows');
  103. end;
  104. --2,使用游标和loop循环来显示所有部门的名称
  105. --游标声明
  106. declare
  107. cursor csr_dept
  108. is
  109. --select语句
  110. select DNAME
  111. from Depth;
  112. --指定行指针,这句话应该是指定和csr_dept行类型相同的变量
  113. row_dept csr_dept%rowtype;
  114. begin
  115. --for循环
  116. for row_dept in csr_dept loop
  117. dbms_output.put_line('部门名称:'||row_dept.DNAME);
  118. end loop;
  119. end;
  120. --3,使用游标和while循环来显示所有部门的的地理位置(用%found属性)
  121. declare
  122. --游标声明
  123. cursor csr_TestWhile
  124. is
  125. --select语句
  126. select LOC
  127. from Depth;
  128. --指定行指针
  129. row_loc csr_TestWhile%rowtype;
  130. begin
  131. --打开游标
  132. open csr_TestWhile;
  133. --给第一行喂数据
  134. fetch csr_TestWhile into row_loc;
  135. --测试是否有数据,并执行循环
  136. while csr_TestWhile%found loop
  137. dbms_output.put_line('部门地点:'||row_loc.LOC);
  138. --给下一行喂数据
  139. fetch csr_TestWhile into row_loc;
  140. end loop;
  141. close csr_TestWhile;
  142. end;
  143. select * from emp
  144. --4,接收用户输入的部门编号,用for循环和游标,打印出此部门的所有雇员的所有信息(使用循环游标)
  145. --CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;
  146. --定义参数的语法如下:Parameter_name [IN] data_type[{:=|DEFAULT} value]
  147. declare
  148. CURSOR
  149. c_dept(p_deptNo number)
  150. is
  151. select * from emp where emp.depno=p_deptNo;
  152. r_emp emp%rowtype;
  153. begin
  154. for r_emp in c_dept(20) loop
  155. dbms_output.put_line('员工号:'||r_emp.EMPNO||'员工名:'||r_emp.ENAME||'工资:'||r_emp.SAL);
  156. end loop;
  157. end;
  158. select * from emp
  159. --5:向游标传递一个工种,显示此工种的所有雇员的所有信息(使用参数游标)
  160. declare
  161. cursor
  162. c_job(p_job nvarchar2)
  163. is
  164. select * from emp where JOB=p_job;
  165. r_job emp%rowtype;
  166. begin
  167. for r_job in c_job('CLERK') loop
  168. dbms_output.put_line('员工号'||r_job.EMPNO||' '||'员工姓名'||r_job.ENAME);
  169. end loop;
  170. end;
  171. SELECT * FROM EMP
  172. --6:用更新游标来为雇员加佣金:(用if实现,创建一个与emp表一摸一样的emp1表,对emp1表进行修改操作),并将更新前后的数据输出出来
  173. --http://zheng12tian.iteye.com/blog/815770
  174. create table emp1 as select * from emp;
  175. declare
  176. cursor
  177. csr_Update
  178. is
  179. select * from emp1 for update OF SAL;
  180. empInfo csr_Update%rowtype;
  181. saleInfo emp1.SAL%TYPE;
  182. begin
  183. FOR empInfo IN csr_Update LOOP
  184. IF empInfo.SAL<1500 THEN
  185. saleInfo:=empInfo.SAL*1.2;
  186. elsif empInfo.SAL<2000 THEN
  187. saleInfo:=empInfo.SAL*1.5;
  188. elsif empInfo.SAL<3000 THEN
  189. saleInfo:=empInfo.SAL*2;
  190. END IF;
  191. UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_Update;
  192. END LOOP;
  193. END;
  194. --7:编写一个PL/SQL程序块,对名字以‘A'或‘S'开始的所有雇员按他们的基本薪水(sal)的10%给他们加薪(对emp1表进行修改操作)
  195. declare
  196. cursor
  197. csr_AddSal
  198. is
  199. select * from emp1 where ENAME LIKE 'A%' OR ENAME LIKE 'S%' for update OF SAL;
  200. r_AddSal csr_AddSal%rowtype;
  201. saleInfo emp1.SAL%TYPE;
  202. begin
  203. for r_AddSal in csr_AddSal loop
  204. dbms_output.put_line(r_AddSal.ENAME||'原来的工资:'||r_AddSal.SAL);
  205. saleInfo:=r_AddSal.SAL*1.1;
  206. UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_AddSal;
  207. end loop;
  208. end;
  209. --8:编写一个PL/SQL程序块,对所有的salesman增加佣金(comm)500
  210. declare
  211. cursor
  212. csr_AddComm(p_job nvarchar2)
  213. is
  214. select * from emp1 where JOB=p_job FOR UPDATE OF COMM;
  215. r_AddComm emp1%rowtype;
  216. commInfo emp1.comm%type;
  217. begin
  218. for r_AddComm in csr_AddComm('SALESMAN') LOOP
  219. commInfo:=r_AddComm.COMM+500;
  220. UPDATE EMP1 SET COMM=commInfo where CURRENT OF csr_AddComm;
  221. END LOOP;
  222. END;
  223. --9:编写一个PL/SQL程序块,以提升2个资格最老的职员为MANAGER(工作时间越长,资格越老)
  224. --(提示:可以定义一个变量作为计数器控制游标只提取两条数据;也可以在声明游标的时候把雇员中资格最老的两个人查出来放到游标中。)
  225. declare
  226. cursor crs_testComput
  227. is
  228. select * from emp1 order by HIREDATE asc;
  229. --计数器
  230. top_two number:=2;
  231. r_testComput crs_testComput%rowtype;
  232. begin
  233. open crs_testComput;
  234. FETCH crs_testComput INTO r_testComput;
  235. while top_two>0 loop
  236. dbms_output.put_line('员工姓名:'||r_testComput.ENAME||' 工作时间:'||r_testComput.HIREDATE);
  237. --计速器减一
  238. top_two:=top_two-1;
  239. FETCH crs_testComput INTO r_testComput;
  240. end loop;
  241. close crs_testComput;
  242. end;
  243. --10:编写一个PL/SQL程序块,对所有雇员按他们的基本薪水(sal)的20%为他们加薪,
  244. --如果增加的薪水大于300就取消加薪(对emp1表进行修改操作,并将更新前后的数据输出出来)
  245. declare
  246. cursor
  247. crs_UpadateSal
  248. is
  249. select * from emp1 for update of SAL;
  250. r_UpdateSal crs_UpadateSal%rowtype;
  251. salAdd emp1.sal%type;
  252. salInfo emp1.sal%type;
  253. begin
  254. for r_UpdateSal in crs_UpadateSal loop
  255. salAdd:= r_UpdateSal.SAL*0.2;
  256. if salAdd>300 then
  257. salInfo:=r_UpdateSal.SAL;
  258. dbms_output.put_line(r_UpdateSal.ENAME||': 加薪失败。'||'薪水维持在:'||r_UpdateSal.SAL);
  259. else
  260. salInfo:=r_UpdateSal.SAL+salAdd;
  261. dbms_output.put_line(r_UpdateSal.ENAME||': 加薪成功.'||'薪水变为:'||salInfo);
  262. end if;
  263. update emp1 set SAL=salInfo where current of crs_UpadateSal;
  264. end loop;
  265. end;
  266. --11:将每位员工工作了多少年零多少月零多少天输出出来
  267. --近似
  268. --CEIL(n)函数:取大于等于数值n的最小整数
  269. --FLOOR(n)函数:取小于等于数值n的最大整数
  270. --truc的用法 http://publish.it168.com/2005/1028/20051028034101.shtml
  271. declare
  272. cursor
  273. crs_WorkDay
  274. is
  275. select ENAME,HIREDATE, trunc(months_between(sysdate, hiredate) / 12) AS SPANDYEARS,
  276. trunc(mod(months_between(sysdate, hiredate), 12)) AS months,
  277. trunc(mod(mod(sysdate - hiredate, 365), 12)) as days
  278. from emp1;
  279. r_WorkDay crs_WorkDay%rowtype;
  280. begin
  281. for r_WorkDay in crs_WorkDay loop
  282. dbms_output.put_line(r_WorkDay.ENAME||'已经工作了'||r_WorkDay.SPANDYEARS||'年,零'||r_WorkDay.months||'月,零'||r_WorkDay.days||'天');
  283. end loop;
  284. end;
  285. --12:输入部门编号,按照下列加薪比例执行(用CASE实现,创建一个emp1表,修改emp1表的数据),并将更新前后的数据输出出来
  286. -- deptno raise(%)
  287. -- 10 5%
  288. -- 20 10%
  289. -- 30 15%
  290. -- 40 20%
  291. -- 加薪比例以现有的sal为标准
  292. --CASE expr WHEN comparison_expr THEN return_expr
  293. --[, WHEN comparison_expr THEN return_expr]... [ELSE else_expr] END
  294. declare
  295. cursor
  296. crs_caseTest
  297. is
  298. select * from emp1 for update of SAL;
  299. r_caseTest crs_caseTest%rowtype;
  300. salInfo emp1.sal%type;
  301. begin
  302. for r_caseTest in crs_caseTest loop
  303. case
  304. when r_caseTest.DEPNO=10
  305. THEN salInfo:=r_caseTest.SAL*1.05;
  306. when r_caseTest.DEPNO=20
  307. THEN salInfo:=r_caseTest.SAL*1.1;
  308. when r_caseTest.DEPNO=30
  309. THEN salInfo:=r_caseTest.SAL*1.15;
  310. when r_caseTest.DEPNO=40
  311. THEN salInfo:=r_caseTest.SAL*1.2;
  312. end case;
  313. update emp1 set SAL=salInfo where current of crs_caseTest;
  314. end loop;
  315. end;
  316. --13:对每位员工的薪水进行判断,如果该员工薪水高于其所在部门的平均薪水,则将其薪水减50元,输出更新前后的薪水,员工姓名,所在部门编号。
  317. --AVG([distinct|all] expr) over (analytic_clause)
  318. ---作用:
  319. --按照analytic_clause中的规则求分组平均值。
  320. --分析函数语法:
  321. --FUNCTION_NAME(<argument>,<argument>...)
  322. --OVER
  323. --(<Partition-Clause><Order-by-Clause><Windowing Clause>)
  324. --PARTITION子句
  325. --按照表达式分区(就是分组),如果省略了分区子句,则全部的结果集被看作是一个单一的组
  326. select * from emp1
  327. DECLARE
  328. CURSOR
  329. crs_testAvg
  330. IS
  331. select EMPNO,ENAME,JOB,SAL,DEPNO,AVG(SAL) OVER (PARTITION BY DEPNO ) AS DEP_AVG
  332. FROM EMP1 for update of SAL;
  333. r_testAvg crs_testAvg%rowtype;
  334. salInfo emp1.sal%type;
  335. begin
  336. for r_testAvg in crs_testAvg loop
  337. if r_testAvg.SAL>r_testAvg.DEP_AVG then
  338. salInfo:=r_testAvg.SAL-50;
  339. end if;
  340. update emp1 set SAL=salInfo where current of crs_testAvg;
  341. end loop;
  342. end;
复制代码

总结

以上就是本文关于Oracle中游标Cursor基本用法详解的全部内容,希望对大家有所帮助,欢迎参阅:oracle数据库导入导出命令解析、ORACLE SQL语句优化技术要点解析、浅谈oracle中单引号转义等,有什么问题可以随时留言,感谢大家!



回复

使用道具 举报