SQL är inte C++, det är inte heller JavaScript. Därför är utvärderingen av logiska uttryck annorlunda, och det här är inte alls samma sak:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Medan du optimerar exekveringsplanen för en PostgreSQL-fråga
Därför, om du fortfarande vill hantera prioritet, måste du strukturellt göra dessa villkor ojämlika med villkorlig
Data och att arbeta med dem är grunden
#0: RTFM
Startande
När utvärderingsordningen är viktig kan den fixas med konstruktionen
CASE
. Till exempel på detta sätt för att undvika division med noll i en meningWHERE
opålitliga:SELECT ... WHERE x > 0 AND y/x > 1.5;
Säkert alternativ:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
Konstruktionen som används
CASE
skyddar uttrycket från optimering, så det bör endast användas när det är nödvändigt.
#1: triggervillkor
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Allt verkar se bra ut, men... Ingen lovar att de investerade SELECT
kommer inte att köras om det första villkoret är falskt. Fixa det med kapslade IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Låt oss nu titta noga - hela kroppen av triggerfunktionen visade sig vara "inlindad". IF
. Och detta betyder att ingenting hindrar oss från att ta bort detta tillstånd från proceduren med WHEN
-betingelser
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Detta tillvägagångssätt låter dig spara serverresurser med en garanti om villkoret är falskt.
#2: ELLER/OCH-kedja
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Annars kan man få att båda EXISTS
kommer att vara sant, men båda kommer att verkställas.
Men om vi vet säkert att en av dem är "sant" mycket oftare (eller "falsk" - för AND
-kedjor) - är det möjligt att på något sätt "öka dess prioritet" så att den andra inte exekveras igen?
Det visar sig att det är möjligt - det algoritmiska tillvägagångssättet ligger nära ämnet för artikeln
Låt oss bara "skjuta under CASE" båda dessa villkor:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
I det här fallet definierade vi inte ELSE
-värde, det vill säga om båda villkoren är falska CASE
kommer tillbaka NULL
, vilket tolkas som FALSE
в WHERE
- betingelser.
Detta exempel kan kombineras på ett annat sätt - efter smak och färg:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: hur man [inte] skriver villkor
Vi ägnade två dagar åt att analysera orsakerna till den "konstiga" triggningen av denna trigger - låt oss se varför.
Källa:
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 ...
Problem #1: Ojämlikhet står inte för NULL
Låt oss anta att allt OLD
-fälten spelade roll NULL
. Vad kommer att hända?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Och ur synvinkel att arbeta fram villkoren NULL
likvärdig FALSE
, som nämnts ovan.
beslutet: använd operator IS DISTINCT FROM
ROW
-operatör, jämför hela poster på en gång:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Problem nummer 2: olika implementering av samma funktionalitet
Låt oss jämföra:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
Varför finns det extra investeringar SELECT
? En funktion to_regclass
? Varför är det annorlunda...
Låt oss fixa:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
Problem #3: bool prioritet
Låt oss formatera källan:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Oj ... Faktum är att det visade sig att i fallet med sanningen i något av de två första tillstånden, så förvandlas hela tillståndet till TRUE
, utan hänsyn till ojämlikheter. Och det här är inte alls vad vi ville.
Låt oss fixa:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
Problem #4 (litet): komplext ELLER-villkor för ett fält
Egentligen hade vi problem i nr 3 just för att det fanns tre förutsättningar. Men istället för dem kan du klara dig med en, med hjälp av mekanismen coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Så är vi NULL
"fånga", och komplex OR
Du behöver inte krångla med parenteser.
Totalt
Låt oss fixa det vi har:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
Och med tanke på att denna triggerfunktion endast kan användas i UPDATE
trigger på grund av närvaron OLD/NEW
i det övre tillståndet kan detta tillstånd i allmänhet tas ut i WHEN
-skick som visas i #1...
Källa: will.com