SQL nėra C++ ir ne JavaScript. Todėl loginių išraiškų apskaičiavimas vyksta skirtingai, ir tai visai nėra tas pats dalykas:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Optimizuojamas PostgreSQL užklausos vykdymo planas
Todėl, jei vis tiek norite valdyti prioritetą, turite jį struktūrizuoti padaryti šias sąlygas nevienodas naudojant sąlyginius
Duomenys ir darbas su jais yra pagrindas
#0: RTFM
Pradedama
Kai vertinimo tvarka yra svarbi, ją galima užfiksuoti naudojant konstrukciją
CASE
. Pavyzdžiui, tai yra būdas išvengti sakinio padalijimo iš nulioWHERE
nepatikimas:SELECT ... WHERE x > 0 AND y/x > 1.5;
Saugus variantas:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
Tokiu būdu naudojamas dizainas
CASE
apsaugo išraišką nuo optimizavimo, todėl ji turėtų būti naudojama tik tada, kai reikia.
#1: paleidimo sąlyga
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Atrodo, kad viskas gerai, bet... To investicijos niekas nežada SELECT
nebus vykdomas, jei pirmoji sąlyga yra klaidinga. Pataisykime su įdėtas IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Dabar pažiūrėkime atidžiai - visas paleidimo funkcijos korpusas yra „apvyniotas“. IF
. Tai reiškia, kad niekas netrukdo mums pašalinti šios sąlygos iš procedūros naudojant WHEN
-sąlygos
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Šis metodas garantuoja serverio išteklių taupymą, kai sąlyga klaidinga.
#2: ARBA/IR grandinė
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Priešingu atveju galite susidurti su abiem EXISTS
bus „tiesa“, bet abu išsipildys.
Bet jei tikrai žinome, kad vienas iš jų yra „tiesa“ daug dažniau (arba „klaidinga“ - už AND
-grandinės) - ar galima kažkaip „padidinti jo prioritetą“, kad antrasis nebūtų vykdomas dar kartą?
Pasirodo, tai įmanoma – algoritminis požiūris artimas straipsnio temai
Tiesiog „įstumkime“ abi šias sąlygas į CASE:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
Šiuo atveju mes neapibrėžėme ELSE
-reikšmė, tai yra, jei abi sąlygos yra klaidingos CASE
grįš NULL
, kuris interpretuojamas kaip FALSE
в WHERE
-sąlygos.
Šis pavyzdys gali būti derinamas kitais būdais - priklausomai nuo skonio ir spalvos:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: kaip [ne] rašyti sąlygas
Mes praleidome dvi dienas analizuodami „keistos“ šio trigerio veikimo priežastis - pažiūrėkime, kodėl.
Šaltinis:
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 problema: nelygybė nepaiso NULL
Įsivaizduokime, kad viskas OLD
-laukai turėjo prasmę NULL
. Kas nutiks?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Ir sąlygų sudarymo požiūriu NULL
lygiavertis FALSE
, kaip paminėta aukščiau.
sprendimas: naudokite operatorių IS DISTINCT FROM
ROW
-operatorius, lyginantis visus įrašus vienu metu:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
2 problema: skirtingi tos pačios funkcijos įgyvendinimai
Palyginti:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
Kodėl čia papildomos investicijos? SELECT
? Funkcija to_regclass
? Kodėl kitaip?..
Pataisykime:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
3 problema: bool operacijų prioritetas
Suformatuokime šaltinį:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Oi... Tiesą sakant, paaiškėjo, kad jei kuri nors iš pirmųjų dviejų sąlygų yra teisinga, visa sąlyga virsta TRUE
, neatsižvelgiant į nelygybes. Ir tai visai ne tai, ko norėjome.
Pataisykime:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
4 problema (maža): sudėtinga ARBA sąlyga vienam laukui
Tiesą sakant, mes turėjome problemų Nr. 3 būtent dėl to, kad buvo trys sąlygos. Tačiau vietoj jų galite apsieiti su vienu, naudodami mechanizmą coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Taigi mes NULL
„pagausime“, ir sunku OR
Nereikia tvoros su kronšteinais.
Iš viso
Įrašykime, ką gavome:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
Ir jei manote, kad šią paleidimo funkciją galima naudoti tik UPDATE
- suaktyvinti dėl prieinamumo OLD/NEW
aukštesnio lygio būsenoje, tada ši sąlyga paprastai gali būti įtraukta WHEN
-Būklė, kaip parodyta Nr.1...
Šaltinis: www.habr.com