SQL no és C++, ni JavaScript. Per tant, el càlcul d'expressions lògiques es produeix de manera diferent, i això no és gens el mateix:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
En procés d'optimització del pla d'execució de consultes PostgreSQL
Per tant, si encara voleu gestionar la prioritat, cal estructurar-la fer que aquestes condicions siguin desiguals utilitzant condicionals
Les dades i el treball amb ells són la base
#0: RTFM
Començant
Quan l'ordre d'avaluació és important, es pot capturar mitjançant el constructe
CASE
. Per exemple, aquesta és una manera d'evitar la divisió per zero en una fraseWHERE
poc fiable:SELECT ... WHERE x > 0 AND y/x > 1.5;
Opció segura:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
El disseny utilitzat d'aquesta manera
CASE
protegeix l'expressió de l'optimització, de manera que només s'ha d'utilitzar quan sigui necessari.
#1: condició d'activació
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Sembla que tot sembla bé, però... Ningú promet que la inversió SELECT
no s'executarà si la primera condició és falsa. Arreglem-ho amb niat IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Ara mirem amb atenció: tot el cos de la funció de disparador està "embolicat". IF
. Això vol dir que res ens impedeix eliminar aquesta condició del procediment d'ús WHEN
- condicions
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Aquest enfocament està garantit per estalviar recursos del servidor quan la condició és falsa.
#2: cadena O/I
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
En cas contrari, podeu acabar amb tots dos EXISTS
serà "veritat", però tots dos es compliran.
Però si sabem del cert que un d'ells és "vertader" molt més sovint (o "fals" - per AND
-cadenes) - és possible d'alguna manera "augmentar la seva prioritat" perquè la segona no es torni a executar?
Resulta que és possible: l'enfocament algorítmic és proper al tema de l'article
Anem a "empènyer" aquestes dues condicions a CASE:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
En aquest cas no hem definit ELSE
-valor, és a dir, si ambdues condicions són falses CASE
tornarà NULL
, que s'interpreta com FALSE
в WHERE
-condicions.
Aquest exemple es pot combinar d'altres maneres, segons el gust i el color:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: com [no] escriure condicions
Vam passar dos dies analitzant els motius de l'operació "estranya" d'aquest disparador; vegem per què.
Font:
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 ...
Problema #1: la desigualtat no respecta NULL
Imaginem que tot OLD
-Els camps tenien significat NULL
. Què passarà?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
I des del punt de vista de treballar les condicions NULL
equivalent FALSE
, com s'ha esmentat anteriorment.
decisió: utilitzar l'operador IS DISTINCT FROM
ROW
-operador, comparant registres sencers alhora:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Problema #2: diferents implementacions de la mateixa funcionalitat
Compareu:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
Per què hi ha una inversió addicional aquí? SELECT
? Una funció to_regclass
? Per què és diferent?...
Arreglem:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
Problema #3: prioritat de les operacions bool
Formatem la font:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Vaja... De fet, va resultar que si alguna de les dues primeres condicions és certa, tota la condició es converteix en TRUE
, sense tenir en compte les desigualtats. I això no és gens el que volíem.
Arreglem:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
Problema #4 (petit): condició OR complexa per a un camp
De fet, vam tenir problemes al número 3 precisament perquè hi havia tres condicions. Però en comptes d'ells, podeu fer-ho amb un, utilitzant el mecanisme coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Així que nosaltres NULL
“Agafarem”, i difícil OR
No cal tancar amb suports.
En total
Enregistrem el que hem aconseguit:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
I si teniu en compte que aquesta funció d'activació només es pot utilitzar en UPDATE
-disparador per disponibilitat OLD/NEW
a la condició de nivell superior, generalment es pot col·locar aquesta condició WHEN
-condició, tal com es mostra al número 1...
Font: www.habr.com