SELECT INTO 建立,名稱為 SQL,自動開啟關閉。假設測試表:
CREATE TABLE student (
stuno VARCHAR2(4),
stuname VARCHAR2(10),
age NUMBER,
gender VARCHAR2(2)
);
CREATE OR REPLACE PROCEDURE proc_stu_while AS
CURSOR cur_stu IS SELECT stuno, stuname FROM student ORDER BY stuno;
v_stuno VARCHAR2(4);
v_stuname VARCHAR2(20);
BEGIN
OPEN cur_stu;
FETCH cur_stu INTO v_stuno, v_stuname;
WHILE cur_stu%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(v_stuno || '->' || v_stuname);
FETCH cur_stu INTO v_stuno, v_stuname;
END LOOP;
CLOSE cur_stu;
END;
/
EXEC proc_stu_while;
CREATE OR REPLACE PROCEDURE proc_stu_if AS
CURSOR cur_stu IS SELECT stuno, stuname FROM student ORDER BY stuno;
v_stuno VARCHAR2(4);
v_stuname VARCHAR2(20);
BEGIN
OPEN cur_stu;
FETCH cur_stu INTO v_stuno, v_stuname;
LOOP
IF cur_stu%FOUND THEN
DBMS_OUTPUT.PUT_LINE(v_stuno || '->' || v_stuname);
FETCH cur_stu INTO v_stuno, v_stuname;
ELSE
EXIT;
END IF;
END LOOP;
CLOSE cur_stu;
END;
/
CREATE OR REPLACE PROCEDURE proc_stu_for AS
CURSOR cur_stu IS SELECT stuno, stuname FROM student ORDER BY stuno;
BEGIN
FOR stu_rec IN cur_stu LOOP
DBMS_OUTPUT.PUT_LINE(stu_rec.stuno || '->' || stu_rec.stuname);
END LOOP;
-- 隱含 %NOTFOUND 檢查,自動關閉
END;
/
-- 甚至可直接內聯游標定義:
BEGIN
FOR stu_rec IN (SELECT stuno, stuname FROM student ORDER BY stuno) LOOP
DBMS_OUTPUT.PUT_LINE(stu_rec.stuno || '->' || stu_rec.stuname);
END LOOP;
END;
/
CREATE OR REPLACE PROCEDURE proc_stu_exit AS
CURSOR cur_stu IS SELECT stuno, stuname FROM student ORDER BY stuno;
v_stuno VARCHAR2(4);
v_stuname VARCHAR2(20);
BEGIN
OPEN cur_stu;
LOOP
FETCH cur_stu INTO v_stuno, v_stuname;
EXIT WHEN cur_stu%NOTFOUND; -- 讀不到資料即跳出
DBMS_OUTPUT.PUT_LINE(v_stuno || '->' || v_stuname);
END LOOP;
CLOSE cur_stu;
END;
/
-- 關鍵優勢:FETCH 在循環首行,避免重複程式碼,符合「讀取-檢查-處理」自然流程
| 屬性 | 顯式游標 | 隱式游標 (SQL) | 含義 |
|---|---|---|---|
| %FOUND | cur_name%FOUND | SQL%FOUND | 最近 FETCH/DML 影響至少一行 |
| %NOTFOUND | cur_name%NOTFOUND | SQL%NOTFOUND | 最近 FETCH/DML 未影響任何行 |
| %ROWCOUNT | cur_name%ROWCOUNT | SQL%ROWCOUNT | 已 FETCH/DML 影響的累計行數 |
| %ISOPEN | cur_name%ISOPEN | SQL%ISOPEN (永遠 FALSE) | 游標是否開啟中 |
CREATE OR REPLACE PROCEDURE proc_upsert_stu (
p_stuno IN student.stuno%TYPE,
p_stuname IN student.stuname%TYPE
) AS
BEGIN
UPDATE student SET stuname = p_stuname WHERE stuno = p_stuno;
IF SQL%NOTFOUND THEN
INSERT INTO student (stuno, stuname) VALUES (p_stuno, p_stuname);
END IF;
END;
/
-- 執行
EXEC proc_upsert_stu('1104', '張燕廣');
CURSOR cur_emp (p_deptno NUMBER) IS
SELECT * FROM emp WHERE deptno = p_deptno;
BEGIN
FOR rec IN cur_emp(10) LOOP ... END LOOP;
FOR rec IN cur_emp(20) LOOP ... END LOOP;
-- 弱型別(動態查詢)
TYPE ref_cur IS REF CURSOR;
PROCEDURE get_data(p_id NUMBER, p_rc OUT ref_cur) AS
BEGIN
IF p_id = 0 THEN
OPEN p_rc FOR SELECT * FROM student;
ELSE
OPEN p_rc FOR SELECT * FROM student WHERE stuno = p_id;
END IF;
END;
-- 強型別(固定回傳結構,編譯期檢查)
TYPE strong_ref_cur IS REF CURSOR RETURN student%ROWTYPE;
DECLARE
TYPE num_arr IS TABLE OF NUMBER;
v_ids num_arr;
BEGIN
SELECT empno BULK COLLECT INTO v_ids FROM emp WHERE deptno = 10;
-- 單次上下文切換取回所有資料
FORALL i IN v_ids.FIRST .. v_ids.LAST
UPDATE emp SET sal = sal * 1.1 WHERE empno = v_ids(i);
-- 單次上下文切換執行所有 DML
END;
/
-- 關鍵:大幅減少 PL/SQL ↔ SQL 引擎切換開銷
EXCEPTION 區塊也 CLOSE,或用 FOR 自動管理。游標是 PL/SQL 處理集合資料的核心機制。掌握 四種循環模式(推薦 EXIT WHEN)、屬性判斷、參數化、REF CURSOR、BULK COLLECT/FORALL,即能編寫高效、健壯、可維護的資料處理邏輯。