Oracle Cursor的使用


When Oracle Database executes a SQL statement, it stores the result set and processing information in an unnamedprivate SQL area. A pointer to this unnamed area, called acursor, lets youretrieve the rows of the result set one at a time.Cursor attributes return information about the state of the cursor.游標是SQL的一個內存工作區,由系統或用戶以變量的形式定義。游標的作用就是用於暫時存儲從數據庫中提取的數據塊。

Cursor類型包括三種: 靜態游標:分為顯式(explicit)游標和隱式(implicit)游標;REF游標:是一種引用類型,類似於指針。
1. 隱式游標
1)Select …INTO…語句,DML語句,使用隱式Cursor。此外,另一種使用FOR LOOP的Implicit Cursor使用方法。
Every time you run either a SQL DML statement or a PL/SQLSELECTINTO statement, PL/SQL opens animplicit cursor. You can get information about this cursor from its attributes, but you cannot control it. After the statement runs, the database closes the cursor; however, its attribute values remain available until another DML orSELECTINTO statement runs. 
2)能夠通過隱式Cusor的屬性來了解操作的狀態和結果。Cursor的屬性包括:
SQL%ROWCOUNT 整型 代表DML語句成功運行的數據行數
SQL%FOUND  布爾型  值為TRUE代表插入、刪除、更新或單行查詢操作成功
SQL%NOTFOUND 布爾型 與SQL%FOUND屬性返回值相反
SQL%ISOPEN 布爾型 DML運行過程中為真,結束后為假
3) 隱式Cursor由系統自己主動打開和關閉.
比如:

set serveroutput on
declare
begin  
  update employees set employee_name='Mike' where employee_id=1001;
  if SQL%FOUND then  
    dbms_output.put_line('Name is updated');
  else
    dbms_output.put_line('Name is not updated');
  end if;
end;
/

 

set serveroutput on
declare
begin  
  for tableInfo in (select * from user_tables) loop
    dbms_output.put_line(tableInfo.table_name);
  end loop;
exception
  when others then
    dbms_output.put_line(sqlerrm);
end;
/


2. 顯式游標
PL/SQL also lets you declare explicit cursors. An explicit cursor has a name and is associated with a query (SQLSELECT statement)—usually one that returns multiple rows. After declaring an explicit cursor, you must open it (with theOPEN statement), fetch rows one at a time from the result set (with theFETCH statement), and close the cursor (with theCLOSE statement). After closing the cursor, you can neither fetch records from the result set nor see the cursor attribute values. 
1) 顯式Cursor的屬性包括:
游標的屬性   返回值類型   意義 
%ROWCOUNT   整型  獲得FETCH語句返回的數據行數 
%FOUND  布爾型 近期的FETCH語句返回一行數據則為真,否則為假 
%NOTFOUND   布爾型 與%FOUND屬性返回值相反 
%ISOPEN 布爾型 游標已經打開時值為真,否則為假  

2) 對於顯式游標的運用分為四個步驟:
a 定義游標---Cursor  [Cursor Name]  IS;
b 打開游標---Open  [Cursor Name]; 
c  操作數據---Fetch  [Cursor name] 
d  關閉游標---Close [Cursor Name]
下面是幾種常見顯式Cursor使用方法。

set serveroutput on declare   cursor cur is select * from user_tables;   tableInfo user_tables%rowtype; begin   open cur;      loop     fetch cur into tableInfo;     exit when cur%notfound;     dbms_output.put_line(tableInfo.table_name);   end loop;

exception   when others then     dbms_output.put_line(sqlerrm);

  close cur; end; /

 

set serveroutput on
declare
  cursor cur is select * from user_tables;
begin  
  for tableInfo in cur loop
    dbms_output.put_line(tableInfo.table_name);
  end loop;
exception
  when others then
    dbms_output.put_line(sqlerrm);
end;
/


還能夠使用帶參數open的cursor。

set serveroutput on declare   cursor cur(tblName varchar2) is select * from user_constraints where table_name=tblName;   tableInfo user_constraints%rowtype; begin   open cur('EMPLOYEES');      loop     fetch cur into tableInfo;     exit when cur%notfound;     dbms_output.put_line(tableInfo.constraint_name);   end loop;

exception   when others then     dbms_output.put_line(sqlerrm);

  close cur; end; /

 

set serveroutput on
declare
  cursor cur(tblName varchar2) is select * from user_constraints where table_name=tblName;
begin
  for tableInfo in cur('EMPLOYEES') loop
    dbms_output.put_line(tableInfo.constraint_name);
  end loop;
exception
  when others then
    dbms_output.put_line(sqlerrm);
end
/


能夠使用WHERE CURRENT OF子句運行UPDATE或DELETE操作。

set serveroutput on
declare
  cursor cur is select * from employees for update;
begin  
  for tableInfo in cur loop
    update employees set salary=salary*1.1 where current of cur;
  end loop;
  commit;
exception
  when others then
    dbms_output.put_line(sqlerrm);
end;
/


3. REF CURSOR(Cursor Variables)
REF Cursor在執行的時候才干確定游標使用的查詢。利用REF CURSOR,能夠在程序間傳遞結果集(一個程序里打開游標變量,在另外的程序里處理數據)。
也能夠利用REF CURSOR實現BULK SQL,提高SQL性能。

REF CURSOR分兩種,Strong REF CURSOR 和 Weak REF CURSOR。

A strong REF CURSOR type specifies a return type, which is the RECORD type of its cursor variables. The PL/SQL compiler does not allow you to use these strongly typed cursor variables for queries that return rows that are not of the return type. Strong REF CURSOR types are less error-prone than weak ones, but weak ones are more flexible.Strong REF CURSOR:指定retrun type,CURSOR變量的類型必須和return type一致。

A weak REF CURSOR type does not specify a return type. The PL/SQL compiler accepts weakly typed cursor variables in any queries. Weak REF CURSOR types are interchangeable; therefore, instead of creating weak REF CURSOR types, you can use the predefined type weak cursor type SYS_REFCURSOR.Weak REF CURSOR:不指定return type,能和不論什么類型的CURSOR變量匹配。

After declaring a cursor variable, you must open it for a specific query (with the OPEN FOR statement), fetch rows one at a time from the result set (with the FETCH statement), and then either close the cursor (with the CLOSE statement) or open it for another specific query (with the OPEN FOR statement). Opening the cursor variable for another query closes it for the previous query. After closing a cursor variable for a specific query, you can neither fetch records from the result set of that query nor see the cursor attribute values for that query.

Ref cursor的使用:
1) Type [Cursor type name] is ref cursor 
2) Open cursor for...
3) Fetch  [Cursor name] 
4) Close Cursor
比如:

Step1:

create or replace package TEST as
  type employees_refcursor_type is ref cursor return employees%rowtype;
  procedure employees_loop(employees_cur IN employees_refcursor_type);
end TEST;
/


Step2:

create or replace package body TEST as
  procedure employees_loop(employees_cur IN employees_refcursor_type) is
    emp employees%rowtype;
  begin
    loop
      fetch employees_cur into emp;
      exit when employees_cur%NOTFOUND;
      dbms_output.put_line(emp.employee_id);
    end loop;
  end employees_loop;
end TEST;
/


Step3:

set serveroutput on
declare
  empRefCur TEST.employees_refcursor_type;
begin
  for i in 10..20 loop
    dbms_output.put_line('Department ID=' || i);
    open empRefCur for select * from employees where department_id=i;
    TEST.employees_loop(empRefCur);
  end loop;
exception
  when others then
    dbms_output.put_line(sqlerrm);
    
  close empRefCur;
end;
/

 
再比如,ref cursor作為輸出參數。

create or replace procedure get_emps(deptno in int, cur out sys_refcursor) as
begin
  open cur for select * from emp where emp.deptno = deptno;
end;

declare
  cur sys_refcursor;
  rec emp%rowtype;
begin
  get_emps(10, cur);
  loop
    fetch cur into rec;
    dbms_output.put_line(rec.ename);
    exit when cur%notfound;
  end loop;
  close cur;
end;

 

4.  BULK SQL
使用FORALL和BULK COLLECT子句。利用BULK SQL能夠降低PLSQL Engine和SQL Engine之間的通信開銷,提高性能。
The PL/SQL features that comprise bulk SQL are the FORALL statement and theBULKCOLLECT clause. TheFORALL statement sends DML statements from PL/SQL to SQL in batches rather than one at a time. TheBULKCOLLECT clause returns results from SQL to PL/SQL in batches rather than one at a time. If a query or DML statement affects four or more database rows, then bulk SQL can significantly improve performance.
1. To speed up INSERT, UPDATE, and DELETE statements, enclose the SQL statement within a PL/SQL FORALL statement instead of a loop construct. 加速INSERT, UPDATE, DELETE語句的運行,也就是用FORALL語句來替代循環語句。
2. To speed up SELECT statements, include the BULK COLLECT INTO clause in the SELECT statement instead of using INTO.  加速SELECT,用BULK COLLECT INTO 來替代INTO。

SQL> create table employees_tmp as select first_name, last_name, salary from employees where 0=1;

set serveroutput on
declare
  cursor employees_cur(depId employees.department_id%type) is select first_name, last_name, salary from employees where department_id=depId;
  type employee_table_type is table of employees_cur%rowtype index by pls_integer;
  employee_table employee_table_type;
begin
  open employees_cur(100);
  fetch employees_cur bulk collect into employee_table;
  close employees_cur;
  
  for i in 1..employee_table.count loop
    dbms_output.put_line(employee_table(i).first_name || ' ' || employee_table(i).last_name || ',' || employee_table(i).salary);
  end loop;
  
  forall i in employee_table.first..employee_table.last
    insert into employees_tmp values(employee_table(i).first_name, employee_table(i).last_name, employee_table(i).salary);
  commit;
end;
/


5.游標的屬性
每一個顯式的游標都有四個屬性:%FOUND、%ISOPEN、%NOTFOUND和%ROWCOUNT。
它們都能返回與靜態或動態SQL語句運行結果相關的實用信息。

為處理SQL數據操作語句,Oracle會打開一個名為SQL的隱式游標。
它的屬性會返回近期一次運行的INSERT、UPDATE、DELETE或單行SELECT的相關信息。

6.  動態性能表V$OPEN_CURSOR
v$open_cursor是oracle提供的一張查看每一個session打開過的cursor的視圖。
在v$open_cursor里面我們能夠看到當前打開的cursor和pga內cached cursor。
11g release 2之后,V$OPEN_CURSOR視圖中添加了CURSOR_TYPE列。包含下面值:
SESSION CURSOR CACHED
PL/SQL CURSOR CACHED
OPEN
OPEN-RECURSIVE
DICTIONARY LOOKUP CURSOR CACHED
BUNDLE DICTIONARY LOOKUP CACHED
能夠通過下面語句了解實際打開的Cursor數量。
select sum(a.value), b.namefrom v$sesstat a, v$statname bwhere a.statistic# = b.statistic#and b.name = 'opened cursors current'group by b.name;

Oracle提供2個參數。
1)open_cursors, 定義每一個Session最大可以打開的游標數量。可以通過select * from v$parameter where name = 'open_cursors'或者show parameter open_cursors查詢。
2)session_cached_cursors 這個參數限制了在pga內session cursor cache list的長度,session cursor cache list是一條雙向的lru(Least Recently Used)鏈表,當一個session打算關閉一個cursor時,假設這個cursor的parse count超過3次,那么這個cursor將會被加到session cursor cache list的MRU端.當一個session打算parse一個sql時,它會先去pga內搜索session cursor cache list,假設找到那么會把這個cursor脫離list,然后當關閉的時候再把這個cursor加到MRU端.session_cached_cursor提供了高速軟分析的功能,提供了比soft parse更高的性能.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM