HAVING 作用於 GROUP BY 分組結果,條件中**只能引用**:常數、聚合函數、GROUP BY 中的欄位。不可引用未分組、未聚合的欄位。
原始慢速寫法:
SELECT a.empno FROM emp a WHERE a.empno NOT IN (SELECT empno FROM emp1 WHERE job='SALE');
改寫為外部聯接(右聯接):
SELECT a.empno FROM emp a, emp1 b WHERE a.empno = b.empno(+) AND b.empno IS NULL AND b.job = 'SALE';
b.empno IS NULL 過濾出無匹配的 emp 列。實測速度明顯提升。注意:NOT IN 遇 NULL 值會返回空結果集,外部聯接無此問題。
DELETE FROM table_name a
WHERE ROWID < (SELECT MAX(ROWID)
FROM table_name b
WHERE b.col1 = a.col1
AND b.col2 = a.col2
...);
大表 (>50萬列) 效能極差**,需改用:
TRUNCATE 原表 → INSERT /*+ APPEND */ 回寫DBMS_REDEFINITION 線上重定義SET TRANSACTION USE ROLLBACK SEGMENT roll_abc; DELETE FROM table_name WHERE ...; COMMIT;
大量刪除/更新交易常報 ORA-01555: snapshot too old,原因是隨機分配的回滾段太小。預先指定大型回滾段確保交易成功。COMMIT 後自動釋放指定。
ALTER INDEX REBUILDTRUNCATE TABLE 同時清除表與索引碎片IMP 導入時視圖可能先於基表建立導致失敗。解法:
# 1. 只導結構,不導資料 imp jfcl/hfjf@ora1 file=expdata.dmp rows=N # 2. 導結構+資料,忽略既有物件錯誤 imp jfcl/hfjf@ora1 file=expdata.dmp full=Y buffer=64000 commit=Y ignore=Y
第一步建立所有表/索引/視圖結構(視圖建立失敗無妨),第二步導入資料並 ignore=Y 跳過物件已存在錯誤,視圖隨基表就緒自動生效。
| # | 場景 | 關鍵語法/原則 | 避坑提醒 |
|---|---|---|---|
| 1 | 分組過濾 | HAVING 只能用聚合/分組欄位 | 別引用明細欄位 |
| 2 | 反向查詢 | 外部聯接 (+) + IS NULL 替代 NOT IN | NOT IN 怕 NULL 值 |
| 3 | 去重 | ROWID 保留 MAX/MIN | 大表必須分批或 TRUNCATE+APPEND |
| 4 | 大交易 | SET TRANSACTION USE ROLLBACK SEGMENT | 指定大回滾段防 ORA-01555 |
| 5 | 索引決策 | 存取 >20% 列數別用索引 | 頻繁 DELETE 必須 REBUILD 索引 |
| 6 | 庫重建 | IMP 兩步走:rows=N → ignore=Y | 視圖依賴基表順序問題 |
這六則經驗來自生產環境無數次踩坑與優化,每一條都能在關鍵時刻節省數小時排查時間。內化為肌肉記憶,是資深 DBA 與開發者的分水嶺。