Oracle儲存過程中游標的簡單使用
阿新 • • 發佈:2019-02-13
初衷:
儲存過程中查詢語句如何返回多行結果?
我們知道,如果儲存過程中查詢語句有多行結果輸出,會報錯:
ORA-01422: exact fetch returns more than requested number of rows
若想讓儲存過程中的查詢語句返回多行結果不報錯,則需要使用遊標來實現。
本例主要也是用來熟悉儲存過程中游標的簡單使用方法。案例所涉及的資料表使用的是oracle自帶的scott使用者,不熟悉scott的也可使用下列指令碼自行建立。
建立表
CREATE TABLE EMP( EMPNO NUMBER(4) NOT NULL
使用案例
--案例1、使用遊標查詢部門編號為10的所有人姓名和薪水 create or replace procedure test2 is begin declare type c is ref cursor; emp_sor c; cname emp.ename%type; csal emp.sal%type; begin open emp_sor for select ename,sal from emp where deptno=10; loop fetch emp_sor into cname,csal; --取遊標的值給變數。 dbms_output.put_line('ename:'||cname||'sal'||csal); exit when emp_sor%notfound; end loop; close emp_sor; end; end test2;
--案例2、直接定義遊標 create or replace procedure test3 is begin declare cursor emp_sor is select ename,sal from emp where deptno=10; cname emp.ename%type; csal emp.sal%type; begin open emp_sor; loop fetch emp_sor into cname,csal; --取遊標的值給變數。 dbms_output.put_line('ename:'||cname||'sal'||csal); exit when emp_sor%notfound; end loop; close emp_sor; end; end test3;
--案例3、使用記錄變數來接受遊標指定的表的資料 create or replace procedure test4 is begin declare cursor emp_sor is select ename, sal from emp where deptno = 10; --使用記錄變數來接受遊標指定的表的資料 type emp_type is record( v_ename emp.ename%type, v_sal emp.sal%type); --用emp_type宣告一個與emp_type類似的記錄變數。該記錄有兩列,與emp表的ename,sal同類型的列。 emp_type1 emp_type; begin open emp_sor; loop fetch emp_sor into emp_type1; --取遊標的值給變數。 dbms_output.put_line(emp_type1.v_ename || ',' || emp_type1.v_sal); exit when emp_sor%notfound; end loop; close emp_sor; end; end test4;
案例4、用for遊標取值 create or replace procedure test5 is begin declare cursor emp_sor is select a.ename from emp a; type ename_table_type is table of varchar2(20); ename_table ename_table_type; begin --用for遊標取值 open emp_sor; --通過bulk collect減少loop處理的開銷,使用Bulk Collect提高Oracle查詢效率 --Oracle8i中首次引入了Bulk Collect特性,該特性可以讓我們在PL/SQL中能使用批查詢,批查詢在某些情況下能顯著提高查詢效率。 --採用bulk collect可以將查詢結果一次性地載入到collections中。 --而不是通過cursor一條一條地處理。 --可以在select into,fetch into,returning into語句使用bulk collect。 --注意在使用bulk collect時,所有的into變數都必須是collections fetch emp_sor bulk collect into ename_table; for i in 1 ..ename_table.count loop dbms_output.put_line(ename_table(i)); end loop; close emp_sor; end; end test5;
--案例5、用for取值,帶隱式遊標會自動開啟和關閉 create or replace procedure test6 is begin declare cursor emp_sor is select a.ename from emp a; type emp_table_type is table of varchar(20); begin for emp_record in emp_sor loop dbms_output.put_line('第'||emp_sor%rowcount||'僱員名:'||emp_record.ename); end loop; end; end test6;
--案例6、判斷遊標是否開啟 create or replace procedure test7 is begin declare cursor emp_sor is select a.ename from emp a; type emp_table_type is table of varchar(20); emp_table emp_table_type; begin --用for取值,判斷遊標是否開啟 if not emp_sor%isopen then open emp_sor; end if; fetch emp_sor bulk collect into emp_table; dbms_output.put_line(emp_sor%rowcount); close emp_sor; end; end test7;
--案例7、使用遊標變數取值 create or replace procedure test8 is begin --使用遊標變數取值 declare cursor emp_sor is select a.ename,a.sal from emp a; emp_record emp_sor%rowtype; begin open emp_sor; loop fetch emp_sor into emp_record; exit when emp_sor%notfound; --exit when emp_sor%notfound放的位置不一樣得到的結果也不一樣。如果放到dbms_....後, --結果會多顯示一行資料,即查詢結果的最後一行顯示了兩次。 dbms_output.put_line('序號'||emp_sor%rowcount||'名稱:'||emp_record.ename||'薪水:'||emp_record.sal); end loop; close emp_sor; end; end test8;
--案例8、帶引數的遊標,在開啟遊標的時候傳入引數 create or replace procedure test9 is begin --帶引數的遊標,在開啟遊標的時候傳入引數 declare cursor emp_sor(no number) is select a.ename from emp a where a.deptno=no; emp_record emp_sor%rowtype; begin open emp_sor(10); loop fetch emp_sor into emp_record; exit when emp_sor%notfound; dbms_output.put_line('序號'||emp_sor%rowcount||'名稱:'||emp_record.ename); end loop; close emp_sor; end; end test9;
--案例9、使用遊標做更新操作 create or replace procedure test10 is begin --使用遊標做更新、刪除操作,必須在定義遊標的時候加上for update --當然也可以用for update nowait declare cursor emp_sor is select a.ename,a.sal from emp a for update; cname emp.ename%type; csal emp.sal%type; begin open emp_sor; loop fetch emp_sor into cname,csal; exit when emp_sor%notfound; dbms_output.put_line('名稱:'||cname||','||'薪水:'||csal); if csal < 2000 then update emp set sal = sal+200 where current of emp_sor; end if; end loop; close emp_sor; --要檢視更新後的資料,必須得重新開啟遊標去查詢 open emp_sor; loop fetch emp_sor into cname,csal; exit when emp_sor%notfound; dbms_output.put_line('名稱:'||cname||','||'new薪水:'||csal); end loop; close emp_sor; end; end test10;
--案例10、使用遊標做刪除操作 create or replace procedure test11 is begin --使用遊標做更新、刪除操作,必須在定義遊標的時候加上for update declare cursor emp_sor is select a.empno from emp a for update; pempno emp.empno%type; begin open emp_sor; loop fetch emp_sor into pempno; exit when emp_sor%notfound; dbms_output.put_line('舊的empno:'||pempno); if pempno = 2009 then delete emp where current of emp_sor; end if; end loop; close emp_sor; --要檢視刪除後的資料,必須得重新開啟遊標去查詢 open emp_sor; loop fetch emp_sor into pempno; exit when emp_sor%notfound; dbms_output.put_line('新的empno:'||pempno); end loop; close emp_sor; end; end test11;
--案例11、直接使用遊標而不用去定義 create or replace procedure test12 is begin for emp_record in(select empno,sal,deptno from emp) loop dbms_output.put_line('員工編號:'||emp_record.empno||',薪水:'||emp_record.sal||',部門編號'||emp_record.deptno); end loop; end test12;
--案例12、帶sql的統計查詢 create or replace procedure test13 is begin declare type test_cursor_type is ref cursor; test_cursor test_cursor_type; v_name user_tables.TABLE_NAME%type; v_count number; str_sql varchar2(100); begin open test_cursor for select table_name from user_tables; loop fetch test_cursor into v_name; if v_name is not null then str_sql := 'select count(*) from '|| v_name; execute immediate str_sql into v_count; end if; exit when test_cursor%notfound; dbms_output.put_line(v_name||','||v_count); end loop; close test_cursor; end; end test13;
當我們寫完儲存過程之後,我們可以在 command window下執行,oracle預設是不顯示輸出的,
所以我們要 set serveroutput on 命令來顯示輸出結果,然後exec test1()即可輸出結果。
轉載自@AlanMathisonTuring