游標是 PL/SQL 處理多行查詢結果的核心機制。本文系統講解隱式/顯式游標、游標屬性、FOR LOOP 簡化寫法、參數化游標、REF CURSOR 動態游標,並附完整代碼示例。
DML (INSERT/UPDATE/DELETE) 和 SELECT INTO 單行查詢自動打開隱式游標,屬性前綴 SQL%:
| 屬性 | 類型 | 含義 |
|---|---|---|
SQL%FOUND | BOOLEAN | 最近 DML/SELECT INTO 影響/返回 ≥1 行 |
SQL%NOTFOUND | BOOLEAN | SQL%FOUND 邏輯反面 |
SQL%ROWCOUNT | NUMBER | 最近 DML 影響行數 / SELECT INTO 返回行數(0/1) |
SQL%ISOPEN | BOOLEAN | 隱式游標始終為 FALSE(執行即關閉) |
SQL%ROWCOUNT 判斷更新結果BEGIN
UPDATE emp SET sal = sal * 1.1 WHERE deptno = 20;
IF SQL%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('無符合條件記錄');
ELSE
DBMS_OUTPUT.PUT_LINE('更新 ' || SQL%ROWCOUNT || ' 行');
END IF;
END;
DECLARE
CURSOR c_emp (p_deptno NUMBER) IS -- 參數化游標
SELECT empno, ename, sal FROM emp WHERE deptno = p_deptno;
v_empno emp.empno%TYPE;
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
OPEN c_emp(20);
LOOP
FETCH c_emp INTO v_empno, v_ename, v_sal;
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_empno || ' ' || v_ename || ' ' || v_sal);
END LOOP;
CLOSE c_emp;
END;
游標名%)| 屬性 | 說明 |
|---|---|
%FOUND | 最近 FETCH 成功返回行 |
%NOTFOUND | 最近 FETCH 無數據 |
%ROWCOUNT | 已提取行數 |
%ISOPEN | 游標是否打開 |
BEGIN
FOR r IN (SELECT empno, ename, sal FROM emp WHERE deptno = 20) LOOP
DBMS_OUTPUT.PUT_LINE(r.empno || ' ' || r.ename || ' ' || r.sal);
END LOOP;
-- 循環結束自動關閉,無需聲明變量
END;
參數化版本:
DECLARE
CURSOR c_emp(p_deptno NUMBER) IS SELECT * FROM emp WHERE deptno = p_deptno;
BEGIN
FOR r IN c_emp(30) LOOP
-- 處理 r.*
END LOOP;
END;
v_sal emp.sal%TYPE:繼承列類型,DDL 變更自動適配。r_emp emp%ROWTYPE:繼承整行結構,適用於 SELECT * INTO r_emp ... 或游標記錄變量。用於存儲過程返回結果集給客戶端(Java/.NET/報表工具)。
CREATE OR REPLACE PROCEDURE get_emp_by_dept(
p_deptno IN NUMBER,
p_rc OUT SYS_REFCURSOR
) AS
BEGIN
OPEN p_rc FOR SELECT empno, ename, sal FROM emp WHERE deptno = p_deptno;
END;
客戶端調用獲取 ResultSet/DataReader 遍歷。
SELECT INTO(隱式游標),多行用顯式游標或 FOR 循環。FOR LOOP 減少樣板代碼,除非需精確控制 OPEN/FETCH/CLOSE 時機。WHERE 過濾,避免全表掃描進內存。BULK COLLECT ... LIMIT 減少上下文切換。REF CURSOR 或 DBMS_SQL。掌握隱式/顯式游標屬性、FOR 循環簡化邏輯、%TYPE/%ROWTYPE 類型錨定、REF CURSOR 跨語言交互,即可覆蓋 95% PL/SQL 游標應用場景。