--游標分2種類型:
--靜態游標:結果集已經存在(靜態定義)的游標,分為隱式和顯示游標
--隱式游標:所有DML語句為隱式游標,通過隱式游標屬性可以獲取SQL語句信息
--顯示游標:用戶顯示聲明的游標,即指定結果集,當查詢返回結果超過一定行時,就需要一個顯示游標
--REF 游標:動態關聯結果集的臨時對象
-強類型:帶return類型
-弱類型: 不帶return類型
--隱式游標
--在PL/SQL中編寫的每條SQL 語句實際上都是隱匿游標。通過在DML操作後使用SQL%ROWCOUNT屬性,可以
--知道語句所改變的行數(INSERT ,UPDATE,DELETE)返回理新行數,SELECT 返回查詢行數.
--顯示游標
--語法:CURSOR 游標名稱 ([參數列表,]) [RETURN 返回值類型]
IS 子查詢(SELECT _statement)
--第一步:聲明游標:
CURSOR 游標名 IS SELECT 。。使用CURSOR定義
--第二步:打開游標
使用OPEN
OPEN 游標名
--第三步:提取游標
使用FETCH 游標 INTO 變量
--第四步:關閉游標
CLOSE 游標名
--顯式游標屬性:
%FOUND 找到是否找到數據,有數據TRUE,沒有則FALSE
%ISOPEN 判斷游標是否打開,打開則返回TRUE,沒有打開則返回FALSE
%NOTFOUND 返回FETCH ...INTO...是否有數據如果沒有返回TRUN,有則為FALSE
%ROWCOUNT 返回執行FETCH 語句所返回的行數,初始為0,每執行一行則%ROWCOUNT增加1
--隱式游標屬性:
SQL%FOUND 找到是否找到數據,有數據TRUE,沒有則FALSE
SQL%ISOPEN 判斷游標是否打開,打開則返回TRUE,沒有打開則返回FALSE
SQL%NOTFOUND 返回FETCH ...INTO...是否有數據如果沒有返回TRUN,有則為FALSE
SQL%ROWCOUNT 返回執行FETCH 語句所返回的行數,初始為0,每執行一行則%ROWCOUNT增加1
隱式游標:
--驗證SQL%ROWCOUNT
DECLARE v_count NUMBER; BEGIN SELECT COUNT(*) INTO v_count FROM dept; --只返回一行結果 dbms_output.put_line('SQL%ROWCOUNT= '|| SQL%ROWCOUNT); END;
結果:SQL%ROWCOUNT= 1
--驗證SLQ%ROWCOUNT並返回行數
DECLARE BEGIN INSERT INTO dept(deptno,dname,loc)VALUES(90,'qqqq','北京'); dbms_output.put_line('SQL%ROWCOUNT= '|| SQL%ROWCOUNT); END;
結果:SQL%ROWCOUNT= 1
--單行隱式游標
DECLARE v_empRow emp%ROWTYPE; BEGIN SELECT * INTO v_empRow FROM emp WHERE empno=7369; IF SQL%FOUND THEN --發現數據 dbms_output.put_line('員工姓名: '|| v_empRow.ename||'職位: '||v_empRow.job); END IF; END;
結果:員工姓名: SMITH職位: CLERK
--多行隱式游標
DECLARE BEGIN UPDATE EMP SET SAL = SAL * 1.2; IF SQL%FOUND THEN --發現數據 DBMS_OUTPUT.PUT_LINE('更新行數' || SQL%ROWCOUNT); ELSE DBMS_OUTPUT.PUT_LINE('更新行數' || SQL%ROWCOUNT); END IF; END;
結果:更新行數14
--顯示游標
--定義游標例1:
DECLARE CURSOR emp_cur IS SELECT empno,ename FROM emp; --定義游標 v_id emp.empno%TYPE; --定義變量ID v_name emp.ename%TYPE; BEGIN OPEN emp_cur ; --打開游標 FETCH emp_cur INTO v_id,v_name; ---提示取游標 LOOP EXIT WHEN emp_cur%notFOUND; --判斷是否還有數據 dbms_output.put_line('員工編號'||v_id||',員工姓名:'||v_name); FETCH emp_cur INTO v_id,v_name; ---提示取游標 END LOOP; CLOSE emp_cur; --關閉游標 END;
結果:
員工編號7369,員工姓名:SMITH 員工編號7499,員工姓名:ALLEN 員工編號7521,員工姓名:WARD 員工編號7566,員工姓名:JONES 員工編號7654,員工姓名:MARTIN 員工編號7698,員工姓名:BLAKE 員工編號7782,員工姓名:CLARK 員工編號7788,員工姓名:SCOTT 員工編號7839,員工姓名:KING 員工編號7844,員工姓名:TURNER 員工編號7876,員工姓名:ADAMS 員工編號7900,員工姓名:JAMES 員工編號7902,員工姓名:FORD 員工編號7934,員工姓名:MILLER
--定義游標例2:
DECLARE V_NAME VARCHAR2(50); --定義變量姓名 V_DNAME VARCHAR2(50); --定義變量部門名稱 CURSOR CUR_E IS --定義游標 SELECT ENAME, DNAME FROM EMP, DEPT WHERE EMP.DEPTNO = DEPT.DEPTNO; BEGIN OPEN CUR_E; --打開游標 LOOP --使用循環來讀取游標 FETCH CUR_E INTO V_NAME, V_DNAME; --提取游標 EXIT WHEN CUR_E%NOTFOUND; --判斷游標是否還有內容 DBMS_OUTPUT.PUT_LINE(CUR_E%ROWCOUNT || ' 員工姓名:' || V_NAME || ' 部門名稱:' || V_DNAME); --輸出內容 END LOOP; CLOSE CUR_E; --關閉游標 END;
結果:
程序結果: 1 員工姓名:SMITH 部門名稱:RESEARCH 2 員工姓名:ALLEN 部門名稱:SALES 3 員工姓名:WARD 部門名稱:SALES 4 員工姓名:JONES 部門名稱:RESEARCH 5 員工姓名:MARTIN 部門名稱:SALES 6 員工姓名:BLAKE 部門名稱:SALES 7 員工姓名:CLARK 部門名稱:ACCOUNTING 8 員工姓名:SCOTT 部門名稱:RESEARCH 9 員工姓名:KING 部門名稱:ACCOUNTING 10 員工姓名:TURNER 部門名稱:SALES 11 員工姓名:ADAMS 部門名稱:RESEARCH 12 員工姓名:JAMES 部門名稱:SALES 13 員工姓名:FORD 部門名稱:RESEARCH 14 員工姓名:MILLER 部門名稱:ACCOUNTING
另一種指定變量類型:
DECLARE V_EMPNAME EMP.ENAME%TYPE; V_DNAME DEPT.DNAME%TYPE; CURSOR EMP_CUR IS SELECT E.ENAME, D.DNAME FROM EMP E, DEPT D WHERE E.DEPTNO = D.DEPTNO; BEGIN OPEN EMP_CUR; LOOP FETCH EMP_CUR INTO V_EMPNAME, V_DNAME; EXIT WHEN EMP_CUR%NOTFOUND; DBMS_OUTPUT.PUT_LINE('員工姓名 :' || V_EMPNAME || ',部門名稱 :' || V_DNAME); END LOOP; CLOSE EMP_CUR; END;
結果同上
--定義游標例3:
DECLARE CURSOR CUR_EMP IS SELECT * FROM EMP; V_EMPROW EMP%ROWTYPE; BEGIN IF CUR_EMP%ISOPEN THEN NULL; ELSE OPEN CUR_EMP; END IF; FETCH CUR_EMP INTO V_EMPROW; WHILE CUR_EMP%FOUND LOOP DBMS_OUTPUT.PUT_LINE('員工姓名: ' || V_EMPROW.ENAME || ',職位: ' || V_EMPROW.JOB || ' ,工資' || V_EMPROW.SAL); FETCH CUR_EMP INTO V_EMPROW; END LOOP; CLOSE CUR_EMP; END;
結果:
員工姓名: SMITH,職位: CLERK ,工資800 員工姓名: ALLEN,職位: SALESMAN ,工資1600 員工姓名: WARD,職位: SALESMAN ,工資1250 員工姓名: JONES,職位: MANAGER ,工資2975 員工姓名: MARTIN,職位: SALESMAN ,工資1250 員工姓名: BLAKE,職位: MANAGER ,工資2850 員工姓名: CLARK,職位: MANAGER ,工資2450 員工姓名: SCOTT,職位: ANALYST ,工資3000 員工姓名: KING,職位: PRESIDENT ,工資5000 員工姓名: TURNER,職位: SALESMAN ,工資1500 員工姓名: ADAMS,職位: CLERK ,工資1100 員工姓名: JAMES,職位: CLERK ,工資950 員工姓名: FORD,職位: ANALYST ,工資3000 員工姓名: MILLER,職位: CLERK ,工資1300
--使用FOR循環
DECLARE CURSOR cur_emp IS SELECT * FROM emp; BEGIN FOR emp_row IN cur_emp LOOP DBMS_OUTPUT.PUT_LINE('員工姓名: ' || emp_row.ENAME || ',職位: ' || emp_row.JOB || ' ,工資' || emp_row.SAL); END LOOP; END;
結果同上
--使用FOR循環操作游標不僅代碼簡單,而且可以將游標的狀態交給系統去完成,盡量使用FOR循環為主
--定義游標例4:使用游標UPDATE數據
--公司上市,決定給員工漲工資,入職年限超過1年加100,1000元封頂
--第一種 直接將計算的結果進行判斷
DECLARE V_ID EMP.EMPNO%TYPE; V_HIREDATE EMP.HIREDATE%TYPE; CURSOR EMP_CUR IS SELECT EMPNO, HIREDATE FROM EMP; BEGIN OPEN EMP_CUR; LOOP FETCH EMP_CUR INTO V_ID, V_HIREDATE; EXIT WHEN EMP_CUR%NOTFOUND; IF (TO_CHAR(SYSDATE, 'yyyy') - TO_CHAR(V_HIREDATE, 'yyyy')) * 100 < 1000 THEN UPDATE EMP SET SAL = SAL + (TO_CHAR(SYSDATE, 'yyyy') - TO_CHAR(V_HIREDATE, 'yyyy')) * 100 WHERE EMPNO = V_ID; DBMS_OUTPUT.PUT_LINE('工資增加成功'); COMMIT; ELSE UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = V_ID; DBMS_OUTPUT.PUT_LINE('工資增加成功'); COMMIT; END IF; END LOOP; CLOSE EMP_CUR; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('數據異常'); ROLLBACK; END;
--第二種通過一個變量判斷
DECLARE V_ID EMP.EMPNO%TYPE; --定義員工編號ID V_HIREDATE EMP.HIREDATE%TYPE; --定義員工入職日期變量 V_SAL EMP.SAL%TYPE; --定義計算每個員工要漲工資的總數變量 CURSOR CUR_EMP IS SELECT EMPNO, HIREDATE --定義游標查詢員工ID和入職日期 FROM EMP; BEGIN IF CUR_EMP%ISOPEN THEN --判斷游標是否打開 NULL; --打開了就什麼也不做 ELSE OPEN CUR_EMP; --沒有打開就打開游標 END IF; LOOP FETCH CUR_EMP INTO V_ID, V_HIREDATE; EXIT WHEN CUR_EMP%NOTFOUND; V_SAL := (TO_CHAR(SYSDATE, 'yyyy') - TO_CHAR(V_HIREDATE, 'yyyy')) * 100; IF V_SAL < 1000 THEN --判斷是否小於1000 UPDATE EMP SET SAL = SAL + V_SAL WHERE EMPNO = V_ID; COMMIT; ELSE --大於1000 UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = V_ID; COMMIT; END IF; END LOOP; CLOSE CUR_EMP; --關閉游標 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('數據異常'); ROLLBACK; --出現異常 就回滾 END;
--定義游標例5
--在動態SELECT中使用游標
DECLARE V_LOWSAL EMP.SAL%TYPE := &LOWSAL; V_HISAL EMP.SAL%TYPE := &HISSAL; CURSOR CUR_EMP IS SELECT * FROM EMP WHERE SAL BETWEEN V_LOWSAL AND V_HISAL; BEGIN FOR EMP_ROW IN CUR_EMP LOOP DBMS_OUTPUT.PUT_LINE('員工姓名: ' || EMP_ROW.ENAME || ',職位: ' || EMP_ROW.JOB || ' ,工資' || EMP_ROW.SAL); END LOOP; END;
--REF動態游標
TYPE 類型名 IS REF CURSOR [RETURN]數據類型
游標名 類型名
OPEN 游標名 FOR 查詢語句
--強類型:帶RETURN
DECLARE TYPE REF_EMP IS REF CURSOR RETURN EMP%ROWTYPE; --定義一個REF動態游標,並返回類型 CUR_EMP REF_EMP; --定義一個變量類型是上面的REF動態游標也稱游標變量 V_EMP EMP%ROWTYPE; --定義一個變量,類型和REF游標返回類型相同,行類型 BEGIN OPEN CUR_EMP FOR SELECT * FROM EMP; --打開游標,並關聯查詢語句 LOOP FETCH CUR_EMP INTO V_EMP; --提取游標數據 EXIT WHEN CUR_EMP%NOTFOUND; DBMS_OUTPUT.PUT_LINE(CUR_EMP%ROWCOUNT||' 員工編號:' || V_EMP.EMPNO || ' 員工姓名:' || V_EMP.ENAME); END LOOP; CLOSE CUR_EMP; END;
--弱類型:不帶RETURN
DECLARE TYPE REF_EMP IS REF CURSOR; --定義一個REF動態游標,並返回類型 CUR_EMP REF_EMP; --定義一個變量類型是上面的REF動態游標也稱游標變量 V_EMP EMP%ROWTYPE; --定義一個變量,類型和REF游標返回類型相同,行類型 V_DEPT DEPT%ROWTYPE; --定義一個變量,類型和REF游標返回類型相同,行類型 BEGIN --員工表 OPEN CUR_EMP FOR SELECT * FROM EMP; --打開游標,並關聯查詢語句 LOOP FETCH CUR_EMP INTO V_EMP; --提取游標數據 EXIT WHEN CUR_EMP%NOTFOUND; DBMS_OUTPUT.PUT_LINE(CUR_EMP%ROWCOUNT || ' 員工編號:' || V_EMP.EMPNO || ' 員工姓名:' || V_EMP.ENAME); END LOOP; CLOSE CUR_EMP; ------------下面是部門表 OPEN CUR_EMP FOR SELECT * FROM DEPT; --打開游標,並關聯查詢語句 LOOP FETCH CUR_EMP INTO V_DEPT; --提取游標數據 EXIT WHEN CUR_EMP%NOTFOUND; DBMS_OUTPUT.PUT_LINE(CUR_EMP%ROWCOUNT || ' 部門編號:' || V_DEPT.DEPTNO || ' 部門名稱:' || V_DEPT.DNAME); END LOOP; CLOSE CUR_EMP; END;
在Oracle9i之後為了方便用戶使用弱類型游標變量,可以使用 SYS_REFCURSOR 來替代 TYPE REF_EMP IS REF CURSOR 上面的聲明可以換為: CUR_EMP SYS_REFCURSOR; --定義一個變量類型是上面的REF動態游標也稱游標變量 V_EMP EMP%ROWTYPE; --定義一個變量,類型和REF游標返回類型相同,行類型 V_DEPT DEPT%ROWTYPE; --定義一個變量,類型和REF游標返回類型相同,行類型
--根據用戶輸入,來輸出內容
DECLARE --TYPE REFC_T IS REF CURSOR; REFC SYS_REFCURSOR; V_ID NUMBER; V_NAME VARCHAR2(50); V_INPUT VARCHAR(1) := UPPER(SUBSTR('&input', 1, 1)); BEGIN IF V_INPUT = 'E' THEN OPEN REFC FOR SELECT EMPNO, ENAME FROM EMP; DBMS_OUTPUT.PUT_LINE('=====員工表信息======'); ELSIF V_INPUT = 'D' THEN OPEN REFC FOR SELECT DEPTNO, DNAME FROM DEPT; DBMS_OUTPUT.PUT_LINE('=====部門表信息======'); ELSE DBMS_OUTPUT.PUT_LINE('=====員工表信息(E)或者部門表信息(D)======='); RETURN; END IF; FETCH REFC INTO V_ID, V_NAME; WHILE REFC%FOUND LOOP DBMS_OUTPUT.PUT_LINE(REFC%ROWCOUNT || '# ' || V_ID || ' ' || V_NAME); FETCH REFC INTO V_ID, V_NAME; END LOOP; CLOSE REFC; END;