SQL ni je C++, niti JavaScript. Zato se izračunavanje logičkih izraza događa drugačije, i ovo — nije isto:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
U procesu optimizacije plana izvršenja upita, PostgreSQL , ne izračunavati neke od njih za pojedine zapise, dodeliti ih uslovu primenjenog indeksa… Ukratko, najbolje je smatrati da unapred ne možete upravljati tim redosledom u kojem će se (i hoće li uopšte) izračunavati ravnopravni uslovi.
Zato, ako želite da upravljate prioritetom, treba strukturalno učiniti ove uslove nerazmjernim uz pomoć uslovnih in .

Podaci i rad sa njima — osnova , stoga nam je važno da operacije nad njima budu izvršene ne samo tačno, već i efikasno. Pogledajmo konkretne primere gde mogu da nastanu greške u izračunavanju izraza, i gde je potrebno poboljšati njihovu efikasnost.
#0: RTFM
Početni :
Kada je redosled izračunavanja važan, može se fiksirati pomoću konstrukcije
CASE. Na primer, ovakav način izbegavanja deljenja sa nulom u izrazuWHEREnije siguran:SELECT ... WHERE x > 0 AND y/x > 1.5;Sigurna varijanta:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;Konstrukcija koja se tako primenjuje
CASEštiti izraz od optimizacije, tako da je treba koristiti samo po potrebi.
#1: условие в триггере
POČNI
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
VRATI NEW;
KRAJ; Sve izgleda dobro, ali… Niko ne garantuje da se ugnježdeni SELECT neće izvršiti kada je prvo stanje lažno. Ispravićemo pomoću ugneždenih IF:
POČNI
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
VRATI NEW;
KRAJ; Sada hajde da pogledamo pažljivo — celo telo triggerske funkcije je „obavijeno“ u IF. A to znači da nas ništa ne sprečava da tu konstrukciju iznesemo iz procedure pomoću :
POČNI
IF EXISTS(SELECT ...) THEN
...
END IF;
VRATI NEW;
KRAJ;
...
KREIRAJ TRIGGER ...
WHEN cond(NEW.fld);Ovakav pristup omogućava da se garantovano štede resursi servera kada je uslov lažan.
#2: OR/AND-цепочка
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B) U neprijatnoj situaciji, može se desiti da oba EXISTS budu „istiniti“, ali oba će se izvršiti..
Ali ako sigurno znamo da je jedan od njih „istinit“ mnogo češće (ili „lažan“ — za AND-chain) — is there a way to "increase its priority" so that the second one doesn't execute unnecessarily?
It turns out, it is possible — the algorithmic approach is relevant to the article's topic. .
Let's just "embed both conditions under CASE":
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END In this case, we haven't defined ELSE-value, meaning in the case of both conditions being false CASE will return NULL, which is interpreted as FALSE u WHERE-condition.
This example can be combined differently — to each their own:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END#3: как [не] надо писать условия
We spent two days analyzing the reasons for this trigger's "strange" behavior — let's see why.
Source code:
IF( NEW."Document_" is null or NEW."Document_" = (select '"Set"'::regclass::oid) or NEW."Document_" = (select to_regclass('"PayrollDocument"')::oid)
AND ( OLD."OurOrganizationDocument" NEW."OurOrganizationDocument"
OR OLD."Deleted" NEW."Deleted"
OR OLD."Date" NEW."Date"
OR OLD."Time" NEW."Time"
OR OLD."CreatedBy" NEW."CreatedBy" ) ) THEN ...Problem #1: inequality does not account for NULL
Let's assume all OLD-fields had a value NULL. What will happen?
SELECT NULL 1 OR NULL 2;
-- NULL From the condition's perspective NULL it is equivalent FALSE, as mentioned above.
Solution: use the operator od ROW-operator, comparing whole records at once:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUEProblem #2: different implementations of the same functionality
Let's compare:
NEW."Document_" = (select '"Set"'::regclass::oid)
NEW."Document_" = (select to_regclass('"PayrollDocument"')::oid) Why these unnecessary nested SELECT? А функция to_regclass? А по-разному-то почему?..
Let's fix it:
NEW."Document_" = '"Set"'::regclass::oid
NEW."Document_" = '"PayrollDocument"'::regclass::oidProblem #3: the priority of bool operations
Let's format the source:
{... IS NULL} OR
{... Set} OR
{... PayrollDocument} AND
( {... inequalities} ) Oops... Essentially, it turns out that if any of the first two conditions are true, the whole condition turns into TRUE, ignoring the inequalities. And that's not what we wanted at all.
Let's fix it:
(
{... IS NULL} OR
{... Set} OR
{... PayrollDocument}
) AND
( {... inequalities} )Problem #4 (small): complex OR-condition for a single field
In fact, the issues in #3 arose precisely because there were three conditions. But instead, one can manage with one, using the coalesce ... IN:
coalesce(NEW."Документ_"::text, '') IN ('', '"Komplet"', '"DokumentPoZarplate"') Tako da ćemo NULL „uhvatiti“, i složenih ILI neće biti potrebno nasilno koristiti zagrade.
Ukupno
Zabeležimo šta smo dobili:
IF (
coalesce(NEW."Dokument_"::text, '') IN ('', '"Komplet"', '"DokumentPoZarplate"') AND
(
OLD."DokumentNasOrganizacija"
, OLD."Obrisan"
, OLD."Datum"
, OLD."Vreme"
, OLD."LiceKreirano"
) IS DISTINCT FROM (
NEW."DokumentNasOrganizacija"
, NEW."Obrisan"
, NEW."Datum"
, NEW."Vreme"
, NEW."LiceKreirano"
)
) THEN ... A ako uzmemo u obzir da se ova triggerska funkcija može primeniti samo u UPDATE-triggeru zbog postojanja OLD/NEW u uslovu najvišeg nivoa, to se stanje može uopšte izdvojiti u WHEN-uslov, kao što je pokazano u #1…
Izvor: habr.com
