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所示...
来源: habr.com