SQL ne estas C++, nek JavaScript. Tial, la taksado de logikaj esprimoj estas malsama, kaj ĉi tio tute ne estas la sama afero:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Optimumante la ekzekutplanon de PostgreSQL-demando
Tial, se vi ankoraŭ volas administri prioritaton, vi devas strukture fari ĉi tiujn kondiĉojn neegalaj kun kondicionalo
Datumoj kaj labori kun ili estas la bazo
#0: RTFM
Komencante
Kiam la ordo de taksado estas grava, ĝi povas esti fiksita per la konstruo
CASE
. Ekzemple, tiamaniere eviti dividon per nulo en frazoWHERE
nefidinda:SELECT ... WHERE x > 0 AND y/x > 1.5;
Sekura opcio:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
La konstruo uzata
CASE
protektas la esprimon de optimumigo, do ĝi devus esti uzata nur kiam necese.
#1: ellasilo kondiĉo
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Ĉio ŝajnas aspekti bone, sed... Neniu promesas, ke la investis SELECT
ne estos ekzekutita se la unua kondiĉo estas falsa. Ripari ĝin per nestis IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Nun ni rigardu atente - la tuta korpo de la ellasilfunkcio montriĝis "envolvita". IF
. Kaj ĉi tio signifas, ke nenio malhelpas nin forigi ĉi tiun kondiĉon de la proceduro uzanta WHEN
-kondiĉoj
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Ĉi tiu aliro permesas ŝpari servilajn rimedojn kun garantio se la kondiĉo estas falsa.
#2: AŬ/KAJ ĉeno
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Alie, oni povas akiri ke ambaŭ EXISTS
estos vera, sed ambaŭ estos ekzekutitaj.
Sed se ni certe scias, ke unu el ili multe pli ofte estas "vera" (aŭ "malvera" - por AND
-ĉenoj) - ĉu eblas iel "pliigi ĝian prioritaton" por ke la dua ne estu ekzekuta denove?
Rezultas, ke ĝi eblas - la algoritme alproksimiĝas al la temo de la artikolo
Ni simple "ŝovu sub KAZON" ambaŭ ĉi tiujn kondiĉojn:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
En ĉi tiu kazo, ni ne difinis ELSE
-valoro, tio estas, se ambaŭ kondiĉoj estas malveraj CASE
revenos NULL
, kiu estas interpretata kiel FALSE
в WHERE
- kondiĉoj.
Ĉi tiu ekzemplo povas esti kombinita alimaniere - por gustumi kaj kolorigi:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: kiel [ne] skribi kondiĉojn
Ni pasigis du tagojn por analizi la kialojn de la "stranga" ekfunkciigo de ĉi tiu ellasilo - ni vidu kial.
Fonto:
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 ...
Problemo #1: Malegaleco ne respondecas pri NULL
Ni supozu, ke ĉio OLD
-kampoj gravis NULL
. Kio okazos?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Kaj el la vidpunkto pri ellaborado de la kondiĉoj NULL
ekvivalento FALSE
, kiel menciite supre.
decido: uzi operatoron IS DISTINCT FROM
ROW
-funkciigisto, komparante tutajn rekordojn samtempe:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Problemo numero 2: malsama efektivigo de la sama funkcieco
Komparu:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
Kial estas ekstraj investoj? SELECT
? Funkcio to_regclass
? Kial ĝi estas malsama...
Ni riparu:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
Problemo #3: bool-preteco
Ni formatu la fonton:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Ho ... Fakte, montriĝis, ke en la kazo de la vero de iu el la unuaj du kondiĉoj, la tuta kondiĉo iĝas TRUE
, ignorante malegalecojn. Kaj ĉi tio tute ne estas tio, kion ni volis.
Ni riparu:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
Problemo #4 (malgranda): kompleksa AŬ kondiĉo por unu kampo
Efektive, ni havis problemojn en n-ro 3 ĝuste ĉar estis tri kondiĉoj. Sed anstataŭ ili, vi povas elteni unu, uzante la mekanismon coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Tiel ni NULL
"kapto", kaj kompleksa OR
Vi ne devas tumulti kun krampoj.
Tuta
Ni riparu tion, kion ni ricevis:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
Kaj konsiderante ke ĉi tiu ellasilfunkcio nur povas esti uzata en UPDATE
ellasilon pro la ĉeesto OLD/NEW
en la supra-nivela kondiĉo, tiam ĉi tiu kondiĉo povas ĝenerale esti elprenita en WHEN
-kondiĉo kiel montrite en #1...
fonto: www.habr.com