1、游标

A.游标概论

游标是一个私有的SQL工作区域,Oracle数据库中有两种游标,分别是隐式游标和显式游标, 隐式游标不易被用户和程序员察觉和意识到,实际上Oracle服务器使用隐式游标来解析和执行提交的SQL 语句; 而显式游标是程序员在程序中显式声明的;通常游标均指显式游标。

B.隐式游标

SQL%ROWCOUNT 受最近的SQL语句影响的行数
SQL%FOUND 最近的SQL语句是否影响了一行以上的 数据
SQL%NOTFOUND 最近的SQL语句是否未影响任何数据
SQL%ISOPEN 对于隐式游标而言永远为FALSE

  1. VARIABLE rows_deleted VARCHAR2(30)
  2. DECLARE
  3. v_employee_id employees.employee_id%TYPE := 176;
  4. BEGIN
  5. DELETE FROM employees
  6. WHERE employee_id = v_employee_id;
  7. :rows_deleted := (SQL%ROWCOUNT ||
  8. ' row deleted.');
  9. END;
  10. /
  11. PRINT rows_deleted

C.显式游标

对于返回多行结果的SQL语句的返回结果,可使用显式游标独立的处理器中每一行的数据。
显式游标的相关函数可以做到:
1、一行一行的处理返回的数据。
2、保持当前处理行的一个跟踪,像一个指针一样指示当前的处理的记录。
3、允许程序员在PLSQL块中人为的控制游标的开启、关闭、上下移动;

D.在程序中对显式游标控制的一般过程

image.png

  1. DECLARE
  2. v_empno employees.employee_id%TYPE;
  3. v_ename employees.last_name%TYPE;
  4. CURSOR emp_cursor IS
  5. SELECT employee_id, last_name
  6. FROM employees;
  7. BEGIN
  8. OPEN emp_cursor;
  9. LOOP
  10. FETCH emp_cursor INTO v_empno, v_ename;
  11. EXIT WHEN emp_cursor%ROWCOUNT > 10 OR
  12. emp_cursor%NOTFOUND;
  13. DBMS_OUTPUT.PUT_LINE (TO_CHAR(v_empno)
  14. ||' '|| v_ename);
  15. END LOOP;
  16. CLOSE emp_cursor;
  17. END ;

For循环进行游标的遍历,For循环省去了游标的 声明、打开、提取、测试、关闭等语句

  1. FOR record_name IN cursor_name LOOP
  2. statement1;
  3. statement2;
  4. . . .
  5. END LOOP;
  1. BEGIN
  2. FOR emp_record IN (SELECT last_name, department_id
  3. FROM employees) LOOP
  4. -- implicit open and implicit fetch occur
  5. IF emp_record.department_id = 80 THEN
  6. ...
  7. END LOOP; -- implicit close occurs
  8. END;
  1. DECLARE
  2. CURSOR emp_cursor IS
  3. SELECT last_name, department_id
  4. FROM employees;
  5. BEGIN
  6. FOR emp_record IN emp_cursor LOOP
  7. -- implicit open and implicit fetch occur
  8. IF emp_record.department_id = 80 THEN
  9. ...
  10. END LOOP; -- implicit close occurs
  11. END;
  12. /

E.游标带参数

  1. CURSOR cursor_name
  2. [(parameter_name datatype, ...)]
  3. IS
  4. select_statement;
  1. DECLARE
  2. CURSOR emp_cursor
  3. (p_deptno NUMBER, p_job VARCHAR2) IS
  4. SELECT employee_id, last_name
  5. FROM employees
  6. WHERE department_id = p_deptno
  7. AND job_id = p_job;
  8. BEGIN
  9. OPEN emp_cursor (80, 'SA_REP');
  10. . . .
  11. CLOSE emp_cursor;
  12. OPEN emp_cursor (60, 'IT_PROG');
  13. . . .
  14. END;

F.FOR UPDATE NOWAIT语句

有的时候打开一个游标是为了更新或者删除一些记录,这种情况下希望在打开游标的时候即锁定相关记录,应该使用for update nowait语句,倘若锁定失败就停止不再继续,以免出现长时间等待资源的死锁情况。

  1. SELECT ...
  2. FROM ...
  3. FOR UPDATE [OF column_reference][NOWAIT];
  1. DECLARE
  2. CURSOR emp_cursor IS
  3. SELECT employee_id, last_name, department_name
  4. FROM employees,departments
  5. WHERE employees.department_id =
  6. departments.department_id
  7. AND employees.department_id = 80
  8. FOR UPDATE OF salary NOWAIT;

G.WHERE CURRENT OF cursor

经常要逐条处理游标中的每一条记录,在循环体内做Update 或者 Delete 时需要有Where指向游标的当前记录可以使用WHERE CURRENT OF cursor

  1. DECLARE
  2. CURSOR sal_cursor IS
  3. SELECT e.department_id, employee_id, last_name, salary
  4. FROM employees e, departments d
  5. WHERE d.department_id = e.department_id
  6. and d.department_id = 60
  7. FOR UPDATE OF salary NOWAIT;
  8. BEGIN
  9. FOR emp_record IN sal_cursor
  10. LOOP
  11. IF emp_record.salary < 5000 THEN
  12. UPDATE employees
  13. SET salary = emp_record.salary * 1.10
  14. WHERE CURRENT OF sal_cursor;
  15. END IF;
  16. END LOOP;
  17. END;
  18. /