FOR ... IN ... LOOP 是 PL/SQL 最常用的迴圈結構。本文展示兩種經典應用:數字範圍迴圈 批次產生日期資料、游標隱性迴圈 動態建表腳本生成,並說明變數作用域、隱性游標優勢與清理最佳實踐。
-- 1. 建立測試表
CREATE TABLE ETL_DATE (
DATE_CHAR VARCHAR2(8),
DATE_DATE DATE
);
-- 2. FOR 迴圈插入 2009/10/01 ~ 2009/10/21
DECLARE
V_DATE DATE;
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE ETL_DATE';
-- 注意:此處 20091001 是數字字面值,非日期
-- 迴圈變數 V_DATE 隱性宣告為 NUMBER,每次遞增 1
FOR V_DATE IN 20091001 .. 20091021 LOOP
INSERT INTO ETL_DATE (DATE_CHAR, DATE_DATE)
VALUES (TO_CHAR(V_DATE), TO_DATE(TO_CHAR(V_DATE), 'YYYYMMDD'));
END LOOP;
COMMIT;
END;
/
-- 3. 驗證
SELECT * FROM ETL_DATE ORDER BY DATE_DATE;
-- 4. 清理
DROP TABLE ETL_DATE;
關鍵限制:此寫法僅適用於同一月份內 (如 1001~1031),跨月會產生 1032、1033 等無效日期。跨月請改用 WHILE + DATE + 1 邏輯。
-- 1. 建立輔助表
CREATE TABLE SQLTEXT (TEXT VARCHAR2(100));
CREATE TABLE HZ (HZ_NAME VARCHAR2(3));
INSERT INTO HZ VALUES ('');
INSERT INTO HZ VALUES ('PRE');
INSERT INTO HZ VALUES ('CUR');
INSERT INTO HZ VALUES ('INS');
INSERT INTO HZ VALUES ('UPD');
-- 2. FOR 迴圈隱性游標遍歷 HZ 表,動態生成建表 SQL
DECLARE
P_TABLE_NAME VARCHAR2(100) := 'CFA';
BEGIN
-- 隱性游標:FOR 變數 IN (SELECT ...) LOOP
FOR HZ_REC IN (SELECT HZ_NAME FROM HZ) LOOP
INSERT INTO SQLTEXT
SELECT 'CREATE TABLE ' || REPLACE(P_TABLE_NAME, 'EDW', 'TMP') ||
HZ_REC.HZ_NAME || ' AS SELECT * FROM ' || P_TABLE_NAME ||
' WHERE ROWNUM < 1'
FROM DUAL;
END LOOP;
END;
/
-- 3. 驗證生成的 SQL
SELECT * FROM SQLTEXT;
-- 輸出範例:
-- CREATE TABLE CFA AS SELECT * FROM CFA WHERE ROWNUM < 1
-- CREATE TABLE CFAPRE AS SELECT * FROM CFA WHERE ROWNUM < 1
-- CREATE TABLE CFACUR AS SELECT * FROM CFA WHERE ROWNUM < 1
-- ...
-- 4. 清理
DROP TABLE SQLTEXT;
DROP TABLE HZ;
| 特性 | FOR 迴圈 (隱性游標) | 顯性游標 (OPEN/FETCH/CLOSE) |
|---|---|---|
| 程式碼行數 | 少 (自動 OPEN/FETCH/CLOSE) | 多 |
| 變數宣告 | 隱性 (Record 型別自動推導) | 需手動宣告 Record/變數 |
| 異常處理 | 自動 CLOSE | 需在 EXCEPTION 中手動 CLOSE |
| 參數化查詢 | 不支援 (查詢固定) | 支援 OPEN cur FOR ... USING ... |
| 動態 SQL | 不支援 | 支援 OPEN cur FOR dynamic_sql |
| 效能 | 相同 (內部相同機制) | 相同 |
OPEN ... FOR ... USINGBULK COLLECT ... LIMIT + FORALL 減少 Context Switch
-- 反向迴圈 (10 遞減到 1)
FOR I IN REVERSE 1 .. 10 LOOP
DBMS_OUTPUT.PUT_LINE(I);
END LOOP;
-- PL/SQL 無 STEP 子句,需用 WHILE 模擬步長
I := 1;
WHILE I <= 10 LOOP
DBMS_OUTPUT.PUT_LINE(I);
I := I + 2; -- 步長 2
END LOOP;