SQL 不是 C++,也不是 JavaScript。 因此,邏輯表達式的求值是不同的,這根本不是一回事:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
優化 PostgreSQL 查詢的執行計劃時
因此,如果你仍然想管理優先級,你需要在結構上 使這些條件不平等 有條件的
數據和使用數據是基礎
#0:RTFM
開始
當評估順序很重要時,可以使用構造來固定它
CASE
。 例如,這種方法可以避免在句子中被零除WHERE
不可靠:SELECT ... WHERE x > 0 AND y/x > 1.5;
安全選項:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
使用的構造
CASE
保護表達式免遭優化,因此僅應在必要時使用它。
#1:觸發條件
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
一切看起來都不錯,但是……沒有人保證所投資的 SELECT
如果第一個條件為 false,則不會執行。 修復它 嵌套的 IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
現在讓我們仔細看看——觸發函數的整個主體竟然被“包裹”在 IF
。 這意味著沒有什麼可以阻止我們使用以下方法從程序中刪除此條件 WHEN
-狀況
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
如果條件為假,此方法可以保證您節省服務器資源。
#2:或/與鏈
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
否則,可得兩者 EXISTS
將會是真的,但是 兩者都將被執行.
但是,如果我們確定其中一個是“正確”的,則更常見(或“錯誤”的——對於 AND
-chains) - 是否有可能以某種方式“提高其優先級”,以便第二個不會再次執行?
事實證明這是可能的——算法方法接近文章的主題
讓我們將這兩個條件“推到 CASE 之下”:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
在這種情況下,我們沒有定義 ELSE
-value,即如果兩個條件都為 false CASE
將返回 NULL
,這被解釋為 FALSE
в WHERE
- 狀況。
這個例子可以用另一種方式組合——味道和顏色:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3:如何[不]寫條件
我們花了兩天的時間來分析這個觸發器“奇怪”觸發的原因——讓我們看看為什麼。
來源:
IF( NEW."Документ_" is null or NEW."Документ_" = (select '"Комплект"'::regclass::oid) or NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
AND ( OLD."ДокументНашаОрганизация" <> NEW."ДокументНашаОрганизация"
OR OLD."Удален" <> NEW."Удален"
OR OLD."Дата" <> NEW."Дата"
OR OLD."Время" <> NEW."Время"
OR OLD."ЛицоСоздал" <> NEW."ЛицоСоздал" ) ) THEN ...
問題#1:不等式不能解釋 NULL
我們假設一切 OLD
-領域很重要 NULL
。 會發生什麼?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
並且從制定條件的角度來說 NULL
相等的 FALSE
, 正如剛才提到的。
解決方法:使用運算符 IS DISTINCT FROM
ROW
- 運算符,一次比較整個記錄:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
問題 2:相同功能的不同實現
讓我們來比較:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
為什麼要有額外的投資 SELECT
? 一個功能 to_regclass
? 為什麼不一樣...
讓我們修復:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
問題#3:布爾優先級
讓我們格式化源:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
哎呀......事實上,事實證明,在前兩個條件中任何一個為真的情況下,整個條件就變成了 TRUE
,不考慮不平等。 這根本不是我們想要的。
讓我們修復:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
問題 #4(小):一個字段的複雜 OR 條件
事實上,我們在第三項中遇到問題正是因為存在三個條件。 但你可以使用以下機制來代替它們 coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
我們也是 NULL
“抓住”,而且複雜 OR
您不必為括號大驚小怪。
在總
讓我們修復我們得到的:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
並且鑑於此觸發功能只能用於 UPDATE
由於存在而觸發 OLD/NEW
在上層條件中,那麼這個條件一般可以在 WHEN
-條件如#1所示...
來源: www.habr.com