SQL is gjin C ++, en net JavaScript. Dêrom komt de berekkening fan logyske útdrukkingen oars foar, en dit is hielendal net itselde:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Yn it proses fan it optimalisearjen fan it PostgreSQL-query-útfierplan
Dêrom, as jo noch prioriteit wolle beheare, moatte jo it strukturearje meitsje dizze betingsten ûngelikense mei help fan betingsten
Gegevens en it wurkjen mei harren binne de basis
#0: RTFM
Starting
As de folchoarder fan evaluaasje wichtich is, kin it fêstlein wurde mei it konstruksje
CASE
. Dit is bygelyks in manier om divyzje troch nul yn in sin te foarkommenWHERE
ûnbetrouber:SELECT ... WHERE x > 0 AND y/x > 1.5;
Feilige opsje:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
It ûntwerp brûkt op dizze manier
CASE
beskermet de útdrukking tsjin optimalisaasje, dus it moat allinich brûkt wurde as it nedich is.
#1: trigger betingst
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Alles liket goed te sjen, mar ... Gjinien belooft dat de ynvestearring SELECT
sil net útfierd wurde as de earste betingst falsk is. Litte wy it reparearje mei nested IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Litte wy no foarsichtich sjen - it heule lichem fan 'e triggerfunksje is "ynpakt". IF
. Dit betsjut dat neat ús foarkomt om dizze betingst te ferwiderjen fan 'e proseduere dy't brûkt wurdt WHEN
-betingsten
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Dizze oanpak is garandearre om serverboarnen te bewarjen as de betingst falsk is.
# 2: OR / EN ketting
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Oars kinne jo einigje mei beide EXISTS
sil wêze "wier", mar beide sille folbrocht wurde.
Mar as wy witte wis dat ien fan harren is "wier" folle faker (of "false" - foar AND
-chains) - is it mooglik om op ien of oare manier "syn prioriteit te fergrutsjen", sadat de twadde net ien kear wer útfierd wurdt?
It docht bliken dat it mooglik is - de algoritmyske oanpak is ticht by it ûnderwerp fan it artikel
Litte wy dizze beide betingsten gewoan "skowe" ûnder CASE:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
Yn dit gefal hawwe wy net definieare ELSE
-wearde, dat is, as beide betingsten falsk binne CASE
sil weromkomme NULL
, dat wurdt ynterpretearre as FALSE
в WHERE
-betingsten.
Dit foarbyld kin op oare manieren kombineare wurde - ôfhinklik fan smaak en kleur:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: hoe [net] betingsten te skriuwen
Wy hawwe twa dagen bestege oan it analysearjen fan de redenen foar de "frjemde" operaasje fan dizze trigger - lit ús sjen wêrom.
Boarne:
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 #1: ûngelikens respektearret NULL net
Lit ús yntinke dat alles OLD
-fjilden hiene betsjutting NULL
. Wat barre sil?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
En út it eachpunt fan it útwurkjen fan de betingsten NULL
lykweardich FALSE
, lykas hjirboppe neamd.
beslút: brûke operator IS DISTINCT FROM
ROW
-operator, it fergelykjen fan folsleine records tagelyk:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Probleem #2: ferskate ymplemintaasjes fan deselde funksjonaliteit
Litte wy fergelykje:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
Wêrom is hjir ekstra ynvestearrings? SELECT
? In funksje to_regclass
? Wêrom is it oars?..
Litte wy it reparearje:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
Probleem # 3: prioriteit fan bool operaasjes
Litte wy de boarne formatearje:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Oeps ... Yn feite, it die bliken dat as ien fan de earste twa betingsten wier is, de hiele betingst feroaret yn TRUE
, sûnder rekken te hâlden mei ûngelikens. En dit is hielendal net wat wy woenen.
Litte wy it reparearje:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
Probleem # 4 (lyts): kompleks OR betingst foar ien fjild
Eins hiene wy problemen yn nûmer 3 krekt om't der trije betingsten wiene. Mar ynstee fan harren kinne jo krije troch mei ien, mei help fan it meganisme coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Wy dus NULL
"wy sille fange", en dreech OR
D'r is gjin need om te fence mei heakjes.
Totaal
Litte wy opnimme wat wy krigen:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
En as jo beskôgje dat dizze triggerfunksje allinich kin wurde brûkt yn UPDATE
-trigger fanwege beskikberens OLD/NEW
yn 'e tastân op it boppeste nivo, dan kin dizze tastân oer it generaal yn pleatst wurde WHEN
-kondysje, lykas werjûn yn #1 ...
Boarne: www.habr.com