INSERT INTO target_table (col1, col2, ...)
SELECT col1, col2, ...
FROM source_table
WHERE condition;
單一 SQL 陳述式完成集合操作,原子性、最佳化、無需游標。
需求:將 components 表中 ID 為 5 的倍數的列,複製到結構相同的 components_copy 表。
DECLARE
v_id components.id%TYPE;
v_sql VARCHAR2(1000);
v_count NUMBER := 0;
-- 定義游標
CURSOR id_cur IS
SELECT id FROM components;
BEGIN
OPEN id_cur;
LOOP
FETCH id_cur INTO v_id;
EXIT WHEN id_cur%NOTFOUND;
-- 條件判斷:ID 為 5 的倍數
IF MOD(v_id, 5) = 0 THEN
BEGIN
-- 動態 SQL 示範(實務建議靜態 SQL)
v_sql := 'INSERT INTO components_copy SELECT * FROM components WHERE id = :1';
EXECUTE IMMEDIATE v_sql USING v_id;
v_count := v_count + 1;
-- 批次提交:每 100 筆提交一次
IF MOD(v_count, 100) = 0 THEN
COMMIT;
END IF;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
NULL; -- 忽略重複鍵
WHEN OTHERS THEN
ROLLBACK; -- 或記錄日誌後決定
RAISE;
END;
END IF;
END LOOP;
CLOSE id_cur;
COMMIT; -- 最終提交
DBMS_OUTPUT.PUT_LINE('Copied: ' || v_count);
EXCEPTION
WHEN OTHERS THEN
IF id_cur%ISOPEN THEN CLOSE id_cur; END IF;
ROLLBACK;
RAISE;
END;
[DECLARE]
-- 宣告區:變數、型別、游標、區域子程序
BEGIN
-- 執行區:程序性與 SQL 陳述式(必要區段)
[EXCEPTION]
-- 例外處理區
END;
; 結束。DECLARE、EXCEPTION 為選擇性。CURSOR cur_name IS SELECT ...OPEN cur_name;FETCH cur_name INTO var_list;CLOSE cur_name;
BEGIN
FOR rec IN (SELECT * FROM components WHERE MOD(id, 5) = 0) LOOP
INSERT INTO components_copy VALUES rec;
-- 隱式游標自動開啟/擷取/關閉,程式碼簡潔
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
| 方案 | 適用場景 | 優勢 |
|---|---|---|
INSERT ... SELECT | 全量/條件複製 | 單一陳述式、原子性、最佳化器最佳路徑 |
FORALL ... BULK COLLECT | 需逐列邏輯處理 | 批次綁定、減少 Context Switch、10-100x 加速 |
CREATE TABLE AS SELECT (CTAS) | 建新表並複製 | 最快、最小記錄、並行執行 |
| 外部表 / Data Pump | 跨資料庫/異構遷移 | 工具級、支援平行、壓縮、加密 |
INSERT ... SELECT、MERGE、CTAS,避免手寫游標迴圈。INSERT /*+ APPEND */ INTO ... 最小化重做日誌。游標是 PL/SQL 基礎技能,但實務中「能用 SQL 解決就不用游標」是效能與可維護性的鐵律。