在 DB2 預存程序中處理結果集逐列邏輯,主要有 FOR 迴圈(隱式游標)與 WHILE + 顯式游標 兩種模式。本文深度剖析兩者語法差異、效能特性、交易控制限制(COMMIT/ROLLBACK 導致游標關閉問題)與 WITH HOLD 解決方案,並給出選型建議。
FOR c1 AS
SELECT deliveryid, deliverycode
FROM delivery
WHERE status = 40
FOR READ ONLY
DO
SELECT SUM(qty) INTO dQty
FROM deliverydetail
WHERE deliveryid = c1.deliveryid;
-- 後續處理...
END FOR;
OPEN/FETCH/CLOSE。c1.欄位名。迴圈內無法執行 COMMIT/ROLLBACK。DB2 在 COMMIT 時會自動關閉所有非 WITH HOLD 游標,而 FOR 迴圈的隱式游標不支援 WITH HOLD,導致 COMMIT 後下一次迭代報錯(游標已關閉)。
DECLARE c1 CURSOR WITH HOLD FOR
SELECT deliveryid, deliverycode
FROM delivery
WHERE status = 40
FOR READ ONLY;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET at_end_c1 = 1;
SET at_end_c1 = 0;
OPEN c1;
FETCH c1 INTO ndeliveryid, sdeliverycode;
WHILE (at_end_c1 = 0) DO
SELECT SUM(qty) INTO dQty
FROM deliverydetail
WHERE deliveryid = ndeliveryid;
-- 此處可安全執行 COMMIT/ROLLBACK
COMMIT; -- 或 ROLLBACK TO SAVEPOINT ...
SET at_end_c1 = 0;
FETCH c1 INTO ndeliveryid, sdeliverycode;
END WHILE;
CLOSE c1;
DECLARE CURSOR WITH HOLD:COMMIT 後保持游標開啟,不被關閉。CONTINUE HANDLER FOR NOT FOUND:優雅處理 FETCH 結束,避免例外中斷。OPEN/FETCH/CLOSE,彈性最高。需求:迴圈處理每筆訂單,成功 COMMIT、失敗 ROLLBACK,繼續下一筆。
WHILE ... DO
SAVEPOINT sp1 ON ROLLBACK RETAIN CURSORS;
-- 業務邏輯
IF 成功 THEN RELEASE SAVEPOINT sp1;
ELSE ROLLBACK TO SAVEPOINT sp1; END IF;
END WHILE;
RETAIN CURSORS 讓 ROLLBACK TO SAVEPOINT 不關閉游標,但仍受限於 COMMIT 行為。
| 場景 | 推薦模式 | 理由 |
|---|---|---|
| 純讀取/單一交易內處理 | FOR 迴圈 | 程式碼最簡潔、維護性佳 |
| 迴圈內需 COMMIT/ROLLBACK | WHILE + WITH HOLD 游標 | 唯一可行方案 |
| 複雜錯誤處理、多重中斷點 | WHILE + 顯式游標 | 完整控制流程與例外處理 |
| 大量資料、效能敏感 | WHILE + WITH HOLD + 批次 FETCH | 可調優 FETCH 批次大小、控制鎖持有時間 |
DB2 預存程序中,FOR 迴圈是預設首選,但一旦涉及「迴圈內獨立交易控制」,必須改用 WHILE + WITH HOLD 顯式游標。理解 COMMIT 關閉游標 這一核心機制,是寫出健壯預存程序的關鍵。