SELECT COUNT(*) INTO V_CNT FROM T WHERE CONDITION;
IF V_CNT > 0 THEN ...
缺陷:僅需布爾判斷卻強制全表/全索引掃描聚合,大表性能災難。
BEGIN
SELECT COL1, COL2 INTO V1, V2 FROM T WHERE CONDITION;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- 不存在分支
NULL;
WHEN OTHERS THEN
RAISE;
END;
UPDATE T SET COL = VAL WHERE CONDITION;
IF SQL%NOTFOUND THEN
INSERT INTO T ...;
END IF;
BEGIN
INSERT INTO T ...;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
UPDATE T SET ... WHERE CONDITION;
END;
MERGE 語句替代SELECT 1 INTO V_FLAG FROM DUAL
WHERE EXISTS (SELECT 1 FROM T WHERE CONDITION);
IF V_FLAG = 1 THEN ...
EXISTS 語義:滿足第一行即停止,不聚合SELECT 1 避免列解析開銷FROM DUAL 保證單行返回FILTER 或 SEMI JOIN,極快-- COUNT(*)
SELECT COUNT(*) FROM T WHERE ID = 100;
-- Plan: INDEX RANGE SCAN -> SORT AGGREGATE
-- EXISTS
SELECT 1 FROM DUAL WHERE EXISTS (SELECT 1 FROM T WHERE ID = 100);
-- Plan: INDEX RANGE SCAN -> FILTER (STOPKEY)
若表僅幾行或單行(如參數表),直接 SELECT COUNT(*) 或 SELECT * 反而更快,因為優化器可能選擇全表掃描且無索引開銷。
CREATE OR REPLACE FUNCTION RECORD_EXISTS(
P_TABLE VARCHAR2,
P_WHERE VARCHAR2
) RETURN BOOLEAN IS
V_SQL VARCHAR2(32767);
V_DUMMY NUMBER;
BEGIN
V_SQL := 'SELECT 1 FROM DUAL WHERE EXISTS (SELECT 1 FROM ' || P_TABLE || ' WHERE ' || P_WHERE || ')';
EXECUTE IMMEDIATE V_SQL INTO V_DUMMY;
RETURN TRUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN RETURN FALSE;
END;
/
-- 調用
IF RECORD_EXISTS('EMP', 'DEPTNO=20 AND SAL>3000') THEN ...
| 業務場景 | 推薦寫法 | 核心優勢 |
|---|---|---|
| 存在即讀取列值 | 直接 SELECT + EXCEPTION | 單次訪問,代碼清晰 |
| 存在即更新,否則插入 | UPDATE + SQL%NOTFOUND | 單語句,無異常開銷 |
| 存在即跳過,否則插入 | INSERT + DUP_VAL_ON_INDEX | 利用約束,並發安全 |
| 純判斷存在性 | EXISTS + DUAL | 最小掃描,語義明確 |
| 極小表/參數表 | COUNT(*) 或 SELECT * | 優化器最優,無索引開銷 |
「不要為了得到 TRUE/FALSE 而付出聚合全表的代價」 — 讓優化器在找到第一匹配行時立即停止。