SQL ez da C++, ezta JavaScript ere. Beraz, adierazpen logikoen ebaluazioa ezberdina da, eta hau ez da batere gauza bera:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
PostgreSQL kontsulta baten exekuzio plana optimizatzen duzun bitartean
Hori dela eta, oraindik lehentasuna kudeatu nahi baduzu, egituraz egin behar duzu baldintza hauek desorekatu baldintzarekin
Datuak eta haiekin lan egitea da oinarria
#0: RTFM
Hasi
Ebaluazio-ordena garrantzitsua denean, konstruktuarekin finkatu daiteke
CASE
. Adibidez, modu honetan esaldi batean zeroz zatitzea saihestekoWHERE
fidagarria:SELECT ... WHERE x > 0 AND y/x > 1.5;
Aukera segurua:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
Erabilitako eraikuntza
CASE
Adierazpena optimizaziotik babesten du, beraz, beharrezkoa denean bakarrik erabili behar da.
#1: trigger-baldintza
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Dena itxura ona dirudi, baina... Inork ez du agintzen inbertitu duenik SELECT
ez da exekutatuko lehen baldintza faltsua bada. Konpondu habiaratu IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Ikus dezagun arretaz: abiarazle-funtzioaren gorputz osoa "bilduta" zegoen IF
. Eta horrek esan nahi du ezerk ez digula eragozten baldintza hau prozedura erabiliz kentzea WHEN
-baldintzak
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Ikuspegi honek zerbitzariaren baliabideak berme batekin gorde ditzakezu baldintza faltsua bada.
#2: EDO/ETA katea
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Bestela, lor daiteke biak EXISTS
egia izango da, baina biak exekutatu egingo dira.
Baina ziur badakigu horietako bat "egia" dela askoz maizago (edo "gezurra" - zeren AND
-kateak) - posible al da nolabait "bere lehentasuna handitzea" bigarrena berriro ere exekutatu ez dadin?
Posible dela ematen du - algoritmikoki hurbilketa artikuluaren gaitik hurbil dago
Eman ditzagun bi baldintza hauek "KASU azpian":
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
Kasu honetan, ez dugu definitu ELSE
-balioa, hau da, baldintza biak faltsuak badira CASE
itzuliko da NULL
, honela interpretatzen dena FALSE
Π² WHERE
- baldintzak.
Adibide hau beste modu batean konbina daiteke - dastatzeko eta koloreztatzeko:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: nola [ez] idatzi baldintzak
Bi egun eman genituen abiarazle honen abiarazte "arraro"aren arrazoiak aztertzen - ikus dezagun zergatik.
Iturria:
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 ...
1. arazoa: Desberdintasunak ez du NULL kontuan hartzen
Demagun dena OLD
-eremuek garrantzia zuten NULL
. Zer gertatuko da?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Eta baldintzak lantzearen ikuspuntutik NULL
baliokidea FALSE
, goian esan bezala.
Erabaki: erabili operadorea IS DISTINCT FROM
ROW
-operadorea, erregistro osoak aldi berean alderatuz:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
2. problema: funtzionalitate beraren ezarpen ezberdina
Dezagun konparatu:
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"'::regclass::oid)
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select to_regclass('"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"')::oid)
Zergatik daude aparteko inbertsioak SELECT
? Funtzio bat to_regclass
? Zergatik da ezberdina...
Konpon dezagun:
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"'::regclass::oid
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"'::regclass::oid
3. arazoa: bool lehentasuna
Formateatu dezagun iturria:
{... IS NULL} OR
{... ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ} OR
{... ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅} AND
( {... Π½Π΅ΡΠ°Π²Π΅Π½ΡΡΠ²Π°} )
Aupa... Izan ere, lehen bi baldintzen egiaren kasuan, baldintza osoa bihurtzen da. TRUE
, desberdintasunak alde batera utzita. Eta hau ez da batere nahi genuena.
Konpon dezagun:
(
{... IS NULL} OR
{... ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ} OR
{... ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅}
) AND
( {... Π½Π΅ΡΠ°Π²Π΅Π½ΡΡΠ²Π°} )
4. arazoa (txikia): eremu baterako EDO baldintza konplexua
Egia esan, 3. zenbakian arazoak izan genituen, hain zuzen ere, hiru baldintza zeudelako. Baina horien ordez, bat egin dezakezu, mekanismoa erabiliz coalesce ... IN
:
coalesce(NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_"::text, '') IN ('', '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"', '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"')
Gu ere bai NULL
"harrapatzea", eta konplexua OR
Ez duzu parentesiekin nahastu behar.
Guztira
Konpon dezagun lortu duguna:
IF (
coalesce(NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_"::text, '') IN ('', '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"', '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"') AND
(
OLD."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ"
, OLD."Π£Π΄Π°Π»Π΅Π½"
, OLD."ΠΠ°ΡΠ°"
, OLD."ΠΡΠ΅ΠΌΡ"
, OLD."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
) IS DISTINCT FROM (
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ"
, NEW."Π£Π΄Π°Π»Π΅Π½"
, NEW."ΠΠ°ΡΠ°"
, NEW."ΠΡΠ΅ΠΌΡ"
, NEW."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
)
) THEN ...
Eta abiarazte-funtzio hau bakarrik erabil daitekeela kontuan hartuta UPDATE
abiarazlea presentzia dela eta OLD/NEW
goi-mailako egoeran, orduan egoera hori orokorrean atera daiteke WHEN
-baldintza #1-n erakusten den moduan...
Iturria: www.habr.com