SQL is geen C++ en ook geen JavaScript. Daarom is de evaluatie van logische uitdrukkingen anders, en dit is helemaal niet hetzelfde:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Terwijl het uitvoeringsplan van een PostgreSQL-query wordt geoptimaliseerd
Dus als je nog prioriteit wilt managen, moet je dat structureel doen maken deze voorwaarden ongelijk met voorwaardelijk
Data en het werken daarmee is de basis
#0: RTFM
Beginnend
Wanneer de volgorde van evaluatie belangrijk is, kan deze worden vastgelegd met het construct
CASE
. Zo voorkom je bijvoorbeeld deling door nul in een zinWHERE
onbetrouwbaar:SELECT ... WHERE x > 0 AND y/x > 1.5;
Veilige optie:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
De gebruikte constructie
CASE
beschermt de expressie tegen optimalisatie, dus deze mag alleen worden gebruikt wanneer dat nodig is.
# 1: triggerconditie
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Alles lijkt er goed uit te zien, maar... Niemand belooft dat de investering SELECT
wordt niet uitgevoerd als de eerste voorwaarde onwaar is. Repareer het met genest IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Laten we nu eens goed kijken - het hele lichaam van de triggerfunctie bleek erin te zijn "verpakt". IF
. En dit betekent dat niets ons ervan weerhoudt om deze aandoening uit de procedure te verwijderen WHEN
-voorwaarden
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Met deze aanpak kunt u serverbronnen opslaan met een garantie als de voorwaarde onwaar is.
#2: OR/AND-ketting
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Anders kan worden verkregen dat beide EXISTS
zal waar zijn, maar beide worden uitgevoerd.
Maar als we zeker weten dat een van hen veel vaker "waar" is (of "onwaar" - voor AND
-ketens) - is het mogelijk om op de een of andere manier "de prioriteit te verhogen" zodat de tweede niet opnieuw wordt uitgevoerd?
Het blijkt mogelijk te zijn - de algoritmische benadering ligt dicht bij het onderwerp van het artikel
Laten we beide voorwaarden gewoon "onder CASE schuiven":
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
In dit geval hebben we niet gedefinieerd ELSE
-waarde, dat wil zeggen, als beide voorwaarden onwaar zijn CASE
zal terugkeren NULL
, wat wordt geïnterpreteerd als FALSE
в WHERE
- voorwaarden.
Dit voorbeeld kan op een andere manier worden gecombineerd - naar smaak en kleur:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: hoe [niet] voorwaarden te schrijven
We hebben twee dagen besteed aan het analyseren van de redenen voor de "vreemde" activering van deze trigger - laten we eens kijken waarom.
Bron:
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: Ongelijkheid houdt geen rekening met NULL
Laten we aannemen dat alles OLD
-velden waren belangrijk NULL
. Wat zal er gebeuren?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
En vanuit het oogpunt van het uitwerken van de voorwaarden NULL
equivalent FALSE
, Zoals hierboven vermeld.
beslissing: gebruik operator IS DISTINCT FROM
ROW
-operator, hele records in één keer vergelijken:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Probleem nummer 2: verschillende implementatie van dezelfde functionaliteit
Laten we het vergelijken:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
Waarom zijn er extra investeringen SELECT
? Een functie to_regclass
? Waarom is het anders...
Laten we het volgende oplossen:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
Probleem # 3: bool-voorrang
Laten we de bron formatteren:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Oeps ... In feite bleek dat in het geval van de waarheid van een van de eerste twee voorwaarden, de hele voorwaarde verandert in TRUE
, zonder rekening te houden met ongelijkheden. En dit is helemaal niet wat we wilden.
Laten we het volgende oplossen:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
Opgave #4 (klein): complexe OF-voorwaarde voor één veld
Eigenlijk hadden we problemen in nr. 3 juist omdat er drie voorwaarden waren. Maar in plaats van hen kun je er een gebruiken, met behulp van het mechanisme coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Zo zijn wij NULL
"vangst", en complex OR
Je hoeft niet te rommelen met haakjes.
In totaal
Laten we repareren wat we hebben:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
En gezien het feit dat deze triggerfunctie alleen kan worden gebruikt in UPDATE
trigger vanwege de aanwezigheid OLD/NEW
in de toestand op het hoogste niveau, dan kan deze toestand over het algemeen worden afgesloten WHEN
-conditie zoals getoond in #1...
Bron: www.habr.com