PostgreSQLren aurkako ereduak: Baldintzen ebaluazioa SQLn

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 Baldintza baliokideak modu arbitrarioan "berrantolatu" ditzake, ez kalkulatu horietako bat erregistro indibidualetarako, aipatu aplikatutako indizearen baldintza... Laburbilduz, modurik errazena zuk suposatzea da. ezin kudeatu ordenan egongo diren (eta zenbatuko diren ala ez) berdinak baldintzak.

Hori dela eta, oraindik lehentasuna kudeatu nahi baduzu, egituraz egin behar duzu baldintza hauek desorekatu baldintzarekin esamoldeak ΠΈ operadoreak.

PostgreSQLren aurkako ereduak: Baldintzen ebaluazioa SQLn
Datuak eta haiekin lan egitea da oinarria gure VLSI konplexuarena, beraz, guretzat oso garrantzitsua da haietako eragiketak zuzen ez ezik, eraginkortasunez ere egitea. Ikus ditzagun adibide zehatzak non adierazpen-ebaluazioan akatsak egin daitezkeen eta non merezi duen haien eraginkortasuna hobetzea.

#0: RTFM

Hasi dokumentaziotik ateratako adibidea:

Ebaluazio-ordena garrantzitsua denean, konstruktuarekin finkatu daiteke CASE. Adibidez, modu honetan esaldi batean zeroz zatitzea saihesteko WHERE 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 PostgreSQLren aurkako ereduak: sarrera arraroa JOIN baten erdira iristen da.

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 tik 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 UPDATEabiarazlea 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

Gehitu iruzkin berria