PL/SQL 中的 SELECT ... INTO 即為隱式游標,僅支援單筆查詢結果。若回傳超過一筆會拋出 TOO_MANY_ROWS;無資料則拋出 NO_DATA_FOUND。
SELECT [DISTINCT | ALL] {* | column[, ...]}
INTO {variable[, ...] | record}
FROM {table | (subquery)} [alias]
WHERE ...;
DECLARE
v_empno SCOTT.EMP.EMPNO%TYPE;
v_ename SCOTT.EMP.ENAME%TYPE;
BEGIN
SELECT empno, ename
INTO v_empno, v_ename
FROM scott.emp
WHERE empno = 7369;
DBMS_OUTPUT.PUT_LINE(v_empno || ' ' || v_ename);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('查無資料');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('資料筆數超過一筆');
END;
/
%TYPE:參考欄位、變數、常數、游標、記錄的資料型別與長度,資料結構變更時自動同步,免修改程式碼。%ROWTYPE:參考整個資料表或游標的列結構,宣告記錄型別變數。DECLARE
v_a NUMBER(5) := 10;
v_b v_a%TYPE := 15; -- 繼承 v_a 型別
v_c v_a%TYPE; -- 未初始化為 NULL
v_emp SCOTT.EMP%ROWTYPE; -- 參考整張表結構
BEGIN
DBMS_OUTPUT.PUT_LINE('v_a='||v_a||' v_b='||v_b||' v_c='||v_c);
END;
/
適用於多筆查詢結果,需經過宣告、開啟、擷取、關閉四步驟。
DECLARE
CURSOR cur_name IS SELECT ...;
v_record cur_name%ROWTYPE;
BEGIN
OPEN cur_name;
LOOP
FETCH cur_name INTO v_record;
EXIT WHEN cur_name%NOTFOUND;
-- 處理 v_record
END LOOP;
CLOSE cur_name;
END;
/
| 屬性 | 說明 |
|---|---|
| %FOUND | 最近一次 FETCH 成功傳回資料 |
| %NOTFOUND | 最近一次 FETCH 未傳回資料 |
| %ROWCOUNT | 已擷取的資料筆數 |
| %ISOPEN | 游標是否處於開啟狀態 |
BEGIN
FOR rec IN (SELECT empno, ename FROM scott.emp WHERE deptno = 20) LOOP
DBMS_OUTPUT.PUT_LINE(rec.empno || ' ' || rec.ename);
END LOOP;
END;
/
DECLARE
CURSOR c_emp(p_deptno NUMBER) IS
SELECT empno, ename FROM scott.emp WHERE deptno = p_deptno;
v_ename VARCHAR2(20);
BEGIN
OPEN c_emp(20);
LOOP
FETCH c_emp INTO v_empno, v_ename;
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_empno || ' ' || v_ename);
END LOOP;
CLOSE c_emp;
END;
/
適用於回傳給呼叫端(如 Java、.NET)或動態組裝 SQL。
TYPE ref_cursor IS REF CURSOR;
v_cur ref_cursor;
OPEN v_cur FOR 'SELECT * FROM emp WHERE deptno = :1' USING 20;
TYPE emp_ref_cur IS REF CURSOR RETURN scott.emp%ROWTYPE;
v_cur emp_ref_cur;
INSERT、UPDATE、DELETE、LOCK TABLE 語法與 SQL 相同,可直接引用宣告區變數,注意巢狀區塊的變數作用域。
CREATE OR REPLACE PROCEDURE fire_employee (p_empno IN NUMBER) AS
v_ename EMP.ENAME%TYPE;
BEGIN
SELECT ename INTO v_ename FROM emp WHERE empno = p_empno;
DELETE FROM emp WHERE empno = p_empno;
DBMS_OUTPUT.PUT_LINE('已刪除員工: ' || v_ename);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('查無該員工編號');
END;
/
SELECT INTO,多筆用顯式游標或 FOR 迴圈。%TYPE/%ROWTYPE 降低維護成本。CLOSE,避免游標洩漏導致 ORA-01000: maximum open cursors exceeded。BULK COLLECT 與 FORALL 提升效能。