SQL ei ole C++ ega ka JavaScript. Seetõttu on loogiliste avaldiste hindamine erinev ja see pole üldse sama asi:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
PostgreSQL-päringu täitmisplaani optimeerimisel
Seega, kui soovite ikkagi prioriteete hallata, peate seda struktuurselt muuta need tingimused ebavõrdseks tingimuslikuga
Andmed ja nendega töötamine on aluseks
# 0: RTFM
Käivitamine
Kui hindamise järjekord on oluline, saab selle konstruktsiooniga fikseerida
CASE
. Näiteks nii, et vältida lauses nulliga jagamistWHERE
ebausaldusväärne:SELECT ... WHERE x > 0 AND y/x > 1.5;
Ohutu valik:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
Kasutatud konstruktsioon
CASE
kaitseb avaldist optimeerimise eest, seega tuleks seda kasutada ainult vajaduse korral.
#1: päästiku tingimus
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Kõik tundub hea olevat, aga... Keegi ei luba, et investeeris SELECT
ei täideta, kui esimene tingimus on vale. Parandage see koos pesastatud IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Vaatame nüüd hoolega – kogu päästikufunktsiooni korpus osutus sisse "mähituks". IF
. Ja see tähendab, et miski ei takista meil seda tingimust protseduurist eemaldamast WHEN
- tingimused
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Selline lähenemine võimaldab säästa serveriressursse garantiiga, kui tingimus on vale.
#2: VÕI/JA kett
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Muidu võib saada, et mõlemad EXISTS
see on tõsi, kuid mõlemad hukatakse.
Aga kui me teame kindlalt, et üks neist on "tõene" palju sagedamini (või "vale" - jaoks AND
-ahelad) - kas on võimalik kuidagi "selle prioriteeti tõsta", et teist uuesti ei täidetaks?
Selgub, et see on võimalik – algoritmiline lähenemine on artikli teemale lähedane
Lükkame need mõlemad tingimused lihtsalt CASE alla:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
Sel juhul me ei määratlenud ELSE
-väärtus, st kui mõlemad tingimused on valed CASE
tuleb tagasi NULL
, mida tõlgendatakse kui FALSE
в WHERE
- tingimused.
Seda näidet saab kombineerida muul viisil - maitsta ja värvida:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: kuidas [mitte] kirjutada tingimusi
Veetsime kaks päeva selle päästiku "kummalise" käivitamise põhjuste analüüsimiseks - vaatame, miks.
Allikas:
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 ...
Probleem nr 1: ebavõrdsus ei arvesta NULL-i
Oletame, et kõik OLD
-väljad olid olulised NULL
. Mis juhtub?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Ja seda tingimuste väljatöötamise seisukohalt NULL
samaväärne FALSE
, nagu eelnevalt mainitud.
otsus: kasutage operaatorit IS DISTINCT FROM
ROW
-operaator, mis võrdleb korraga terveid kirjeid:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Probleem number 2: sama funktsiooni erinev rakendamine
Võrrelda:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
Miks on lisainvesteeringuid SELECT
? Funktsioon to_regclass
? Miks on teisiti...
Parandame:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
Probleem nr 3: tõeväärtus
Vormindame allika:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Oeh ... Tegelikult selgus, et kahe esimese tingimuse tõesuse korral muutub kogu tingimus TRUE
, arvestamata ebavõrdsust. Ja see pole üldse see, mida me tahtsime.
Parandame:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
Ülesanne nr 4 (väike): keeruline VÕI tingimus ühe välja jaoks
Tegelikult oli meil nr 3 probleeme just seetõttu, et seal oli kolm tingimust. Kuid nende asemel saate mehhanismi kasutades hakkama ka ühega coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Nii ka meie NULL
"saak" ja keeruline OR
Sulgudega ei pea pabistama.
Kogusummas
Parandame selle, mis meil on:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
Ja arvestades, et seda päästikufunktsiooni saab kasutada ainult UPDATE
kohaloleku tõttu vallandada OLD/NEW
ülemise taseme seisundis, siis saab selle tingimuse üldiselt välja võtta WHEN
- seisukord nagu näidatud #1...
Allikas: www.habr.com