SQL 調優不僅是 DBA 的事,**開發人員寫 SQL 的習慣決定了 80% 的效能上限**。本文萃取三條**零成本、立竿見影、普遍適用**的寫碼鐵律,助您在編碼階段就寫出高效能 SQL。
Oracle 解析 WHERE 子句**自下而上 (從最後一行向上)**。雖 CBO 會自動重排,但**明確排序能輔助優化器、穩定計畫、利於 Hint 干預**。
-- 低效:弱過濾在前,強過濾在後 (執行 156.3 秒)
SELECT * FROM emp e
WHERE sal > 50000
AND job = 'MANAGER'
AND 25 < (SELECT COUNT(*) FROM emp WHERE mgr = e.empno);
-- 高效:強過濾 (子查詢) 前置,弱過濾後置 (執行 10.6 秒)
SELECT * FROM emp e
WHERE 25 < (SELECT COUNT(*) FROM emp WHERE mgr = e.empno)
AND sal > 50000
AND job = 'MANAGER';
現代補充: CBO 11g+ 已能自動重排,**但複雜視圖/子查詢/外部連接時,手工排序仍具穩定計畫價值**。
-- 惡例
SELECT * FROM orders WHERE cust_id = 123;
-- 佳例
SELECT order_id, order_date, total_amt, status FROM orders WHERE cust_id = 123;
每次 SQL 執行 = **網路 RTT + 解析/綁定/執行/獲取開銷**。減少 Round-trip 是提升吞吐的核心槓桿。
| 模式 | Round-trip | 適用場景 | 範例 |
|---|---|---|---|
| 單筆循環 | N 次 | 極少數據、邏輯極複雜 | `for id in ids: UPDATE ... WHERE id = ?` |
| 批次繫結 | 1 次 | 大量 DML (INSERT/UPDATE/DELETE) | `PreparedStatement.addBatch()` / `FORALL` |
| 集合操作 (SQL) | 1 次 | 集合邏輯可用 SQL 表達 | `INSERT SELECT` / `MERGE` / `DELETE WHERE IN` |
| PL/SQL 封裝 | 1 次 | 複雜業務邏輯、多步驟、需事務原子性 | `CALL pkg_batch.process(:param)` |
| Pipeline Function | 1 次 | 大量資料轉換/產生,流式返回客戶端 | `SELECT * FROM TABLE(pkg_gen.data(:p))` |
String sql = "INSERT INTO log (id, msg, created) VALUES (seq_log.nextval, ?, SYSTIMESTAMP)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
for (LogEntry e : entries) {
ps.setString(1, e.getMsg());
ps.addBatch();
}
int[] counts = ps.executeBatch(); // 單次網路往返執行所有
conn.commit();
}
DECLARE
TYPE t_ids IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
v_ids t_ids;
BEGIN
-- 假設 v_ids 已填充 10000 筆
FORALL i IN v_ids.FIRST .. v_ids.LAST
UPDATE accounts SET balance = balance - 100 WHERE id = v_ids(i);
-- 單次上下文切換,批次執行
END;
-- 重構前:N+1 問題、SELECT *、弱過濾前置
FOR rec IN (SELECT * FROM orders WHERE status = 'NEW') LOOP
SELECT * FROM customers WHERE cust_id = rec.cust_id; -- 循環查詢
UPDATE orders SET status = 'PROCESSING' WHERE order_id = rec.order_id; -- 循環更新
END LOOP;
-- 重構後:單次 SQL、明確欄位、集合操作
MERGE INTO orders o
USING (SELECT order_id FROM orders WHERE status = 'NEW') src
ON (o.order_id = src.order_id)
WHEN MATCHED THEN UPDATE SET status = 'PROCESSING',
processed_dt = SYSTIMESTAMP
WHERE o.status = 'NEW'; -- 單次執行,原子性保證
這三條鐵律不需要權限、不需要工具、不需要重啟,**只需要開發習慣**。從今天的第一行 SQL 開始貫徹,您的系統效能基線將顯著上移。