游標是 PL/SQL 處理多筆查詢結果的核心機制。本文詳細說明明確游標的五個標準步驟、屬性應用、FOR 迴圈簡化寫法、參數化游標與 REF CURSOR 進階用法,並提供完整範例程式碼。
table.column%TYPE 錨定)。OPEN cursor_name [ (parameters) ]; 執行查詢、建立結果集指標。FETCH cursor_name INTO var1, var2, ...; 逐筆讀取至變數。CLOSE cursor_name; 釋放資源、游標變數可重複開啟。CREATE OR REPLACE PROCEDURE sel_person IS
v_id person.id%TYPE;
v_name person.pname%TYPE;
v_birthday person.birthday%TYPE;
CURSOR c_person IS
SELECT id, pname, birthday FROM person;
BEGIN
OPEN c_person;
LOOP
FETCH c_person INTO v_id, v_name, v_birthday;
EXIT WHEN c_person%NOTFOUND; -- 關鍵:判斷是否讀完
DBMS_OUTPUT.PUT_LINE(v_id || '----' || v_name || '----' || v_birthday);
END LOOP;
CLOSE c_person;
END sel_person;
/
| 屬性 | 意義 | 常見用法 |
|---|---|---|
%FOUND | 最近一次 FETCH 成功傳回資料 | IF c_person%FOUND THEN ... |
%NOTFOUND | 最近一次 FETCH 未傳回資料 (已讀完) | EXIT WHEN c_person%NOTFOUND; |
%ROWCOUNT | 累計已 FETCH 的筆數 | DBMS_OUTPUT.PUT_LINE('Total: ' || c_person%ROWCOUNT); |
%ISOPEN | 游標是否處於開啟狀態 | IF NOT c_person%ISOPEN THEN OPEN c_person; END IF; |
Oracle 自動處理 OPEN/FETCH/CLOSE,程式碼最精簡:
BEGIN
FOR rec IN (SELECT id, pname, birthday FROM person) LOOP
DBMS_OUTPUT.PUT_LINE(rec.id || '----' || rec.pname || '----' || rec.birthday);
END LOOP;
END;
/
rec 為隱性宣告的 ROWTYPE,欄位名同查詢別名或表欄位名。CURSOR c_emp_by_dept (p_deptno NUMBER) IS
SELECT empno, ename, sal FROM emp WHERE deptno = p_deptno;
BEGIN
FOR rec IN c_emp_by_dept(10) LOOP -- 傳入參數
DBMS_OUTPUT.PUT_LINE(rec.empno || ' ' || rec.ename);
END LOOP;
-- 同一游標可再次開啟傳入不同參數
FOR rec IN c_emp_by_dept(20) LOOP
...
END LOOP;
END;
TYPE t_ref_cursor IS REF CURSOR; -- 弱型別 REF CURSOR
TYPE t_emp_ref IS REF CURSOR RETURN emp%ROWTYPE; -- 強型別
PROCEDURE get_emp_data (p_deptno IN NUMBER, p_rc OUT t_ref_cursor) IS
BEGIN
OPEN p_rc FOR SELECT * FROM emp WHERE deptno = p_deptno;
END;
應用場景:Java/.NET 呼叫 Stored Procedure 取得 ResultSet、動態 SQL 組裝查詢條件。
CURSOR c_emp_lock IS
SELECT empno, sal FROM emp WHERE deptno = 20 FOR UPDATE OF sal;
BEGIN
FOR rec IN c_emp_lock LOOP
UPDATE emp SET sal = sal * 1.1
WHERE CURRENT OF c_emp_lock; -- 只更新目前游標指向的列
END LOOP;
COMMIT;
END;
FOR UPDATE 於 OPEN 時對結果集加排他鎖 (可加 NOWAIT/WAIT n)。WHERE CURRENT OF 避免重複 WHERE 條件,確保更新正是當前列。| 錯誤現象 | 原因 | 解決 |
|---|---|---|
ORA-01001: invalid cursor | 游標未 OPEN 或已 CLOSE 又 FETCH | 檢查 OPEN/CLOSE 配對、迴圈邏輯 |
ORA-06511: PL/SQL: cursor already open | 重複 OPEN 同一游標變數 | 先 CLOSE 或改用 FOR 迴圈 |
| 游標變數在程序間傳遞失效 | REF CURSOR 必須在同一 Session/Call 內使用 | 確保 Client 端立即消費結果集 |
熟練掌握游標五步驟、屬性判斷、FOR 迴圈簡化寫法與 REF CURSOR 進階應用,是編寫高品質 PL/SQL 程式的基本功。實務建議:優先使用 FOR 迴圈游標,僅在需精確控制開啟/關閉時機、跨程序傳遞結果集或逐列鎖定更新時,才使用明確游標。