Antipatterns za PostgreSQL: Tathmini ya Masharti katika SQL

SQL sio C++, na sio JavaScript. Kwa hivyo, hesabu ya misemo ya kimantiki hufanyika tofauti, na hii sio kitu sawa:

WHERE fncondX() AND fncondY()

= fncondX() && fncondY()

Katika mchakato wa kuboresha mpango wa utekelezaji wa swala la PostgreSQL inaweza "kupanga upya" hali sawa kiholela, usihesabu baadhi yao kwa rekodi za kibinafsi, yanahusiana na masharti ya index iliyotumiwa ... Kwa kifupi, njia rahisi ni kudhani kuwa wewe haiwezi kudhibiti watahesabiwa kwa utaratibu gani (na kama watahesabiwa kabisa) sawa masharti.

Kwa hivyo, ikiwa bado unataka kusimamia kipaumbele, unahitaji kuitengeneza kufanya masharti haya kutokuwa sawa kwa kutumia masharti maneno ΠΈ waendeshaji.

Antipatterns za PostgreSQL: Tathmini ya Masharti katika SQL
Data na kufanya kazi nao ni msingi tata yetu ya VLSI, kwa hiyo ni muhimu sana kwetu kwamba shughuli juu yao hazifanyiki kwa usahihi tu, bali pia kwa ufanisi. Hebu tuangalie mifano maalum ambapo makosa katika kuhesabu maneno yanaweza kufanywa, na ambapo inafaa kuboresha ufanisi wao.

#0: RTFM

Kuanzia mfano kutoka kwa nyaraka:

Wakati mpangilio wa tathmini ni muhimu, inaweza kunaswa kwa kutumia muundo CASE. Kwa mfano, hii ni njia ya kuzuia mgawanyiko kwa sifuri katika sentensi WHERE asiyeaminika:

SELECT ... WHERE x > 0 AND y/x > 1.5;

Chaguo salama:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

Ubunifu uliotumika kwa njia hii CASE hulinda usemi dhidi ya uboreshaji, kwa hivyo inapaswa kutumika tu inapohitajika.

#1: hali ya kichochezi

BEGIN
  IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
    ...
  END IF;
  RETURN NEW;
END;

Kila kitu kinaonekana kuwa kizuri, lakini ... Hakuna mtu anayeahidi kwamba uwekezaji SELECT haitatekelezwa ikiwa sharti la kwanza ni la uwongo. Hebu turekebishe na kiota IF:

BEGIN
  IF cond(NEW.fld) THEN
    IF EXISTS(SELECT ...) THEN
      ...
    END IF;
  END IF;
  RETURN NEW;
END;

Sasa hebu tuangalie kwa uangalifu - mwili mzima wa kazi ya trigger "imefungwa" ndani IF. Hii ina maana kwamba hakuna kitu kinatuzuia kuondoa hali hii kutoka kwa utaratibu wa kutumia WHEN-masharti:

BEGIN
  IF EXISTS(SELECT ...) THEN
    ...
  END IF;
  RETURN NEW;
END;
...
CREATE TRIGGER ...
  WHEN cond(NEW.fld);

Mbinu hii imehakikishwa kuokoa rasilimali za seva wakati hali si kweli.

#2: AU/NA mnyororo

SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)

Vinginevyo, unaweza kuishia na zote mbili EXISTS itakuwa "kweli", lakini zote mbili zitatimizwa.

Lakini ikiwa tunajua kwa hakika kuwa mmoja wao ni "kweli" mara nyingi zaidi (au "uongo" - kwa AND-minyororo) - inawezekana kwa namna fulani "kuongeza kipaumbele chake" ili ya pili isitekelezwe tena?

Inageuka kuwa inawezekana - mbinu ya algorithmic iko karibu na mada ya makala PostgreSQL Antipatterns: rekodi adimu itafikia katikati ya JIUNGE.

Hebu "tusukume" masharti haya yote mawili chini ya KESI:

SELECT ...
WHERE
  CASE
    WHEN EXISTS(... A) THEN TRUE
    WHEN EXISTS(... B) THEN TRUE
  END

Katika kesi hii hatukufafanua ELSE-thamani, yaani, ikiwa masharti yote mawili ni ya uwongo CASE itarudi NULL, ambayo inafasiriwa kama FALSE Π² WHERE-masharti.

Mfano huu unaweza kuunganishwa kwa njia zingine - kulingana na ladha na rangi:

SELECT ...
WHERE
  CASE
    WHEN NOT EXISTS(... A) THEN EXISTS(... B)
    ELSE TRUE
  END

#3: jinsi [si] kuandika masharti

Tulitumia siku mbili kuchambua sababu za operesheni "ya kushangaza" ya kichochezi hiki - wacha tuone ni kwanini.

Chanzo:

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 ...

Tatizo #1: ukosefu wa usawa hauheshimu NULL

Hebu fikiria kwamba kila kitu OLD-mashamba yalikuwa na maana NULL. Nini kitatokea?

SELECT NULL <> 1 OR NULL <> 2;
-- NULL

Na kutoka kwa mtazamo wa kufanya kazi nje ya masharti NULL sawa FALSE, kama ilivyoelezwa hapo juu.

uamuzi: tumia opereta IS DISTINCT FROM kutoka ROW- mwendeshaji, kulinganisha rekodi nzima mara moja:

SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE

Tatizo #2: utekelezaji tofauti wa utendakazi sawa

Hebu tulinganishe:

NEW."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚_" = (select '"ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚"'::regclass::oid)
NEW."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚_" = (select to_regclass('"Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠŸΠΎΠ—Π°Ρ€ΠΏΠ»Π°Ρ‚Π΅"')::oid)

Kwa nini kuna uwekezaji wa ziada hapa? SELECT? Kitendaji to_regclass? Kwa nini ni tofauti? ..

Hebu turekebishe:

NEW."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚_" = '"ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚"'::regclass::oid
NEW."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚_" = '"Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠŸΠΎΠ—Π°Ρ€ΠΏΠ»Π°Ρ‚Π΅"'::regclass::oid

Tatizo #3: kipaumbele cha uendeshaji wa bool

Wacha tupange chanzo:

{... IS NULL} OR
{... ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚} OR
{... Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠŸΠΎΠ—Π°Ρ€ΠΏΠ»Π°Ρ‚Π΅} AND
( {... нСравСнства} )

Lo ... Kwa kweli, ikawa kwamba ikiwa yoyote ya masharti mawili ya kwanza ni ya kweli, hali nzima inageuka TRUE, bila kuzingatia usawa. Na hii sio kabisa tuliyotaka.

Hebu turekebishe:

(
  {... IS NULL} OR
  {... ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚} OR
  {... Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠŸΠΎΠ—Π°Ρ€ΠΏΠ»Π°Ρ‚Π΅}
) AND
( {... нСравСнства} )

Tatizo #4 (ndogo): changamano AU hali ya sehemu moja

Kweli, tulikuwa na matatizo katika Nambari 3 kwa usahihi kwa sababu kulikuwa na masharti matatu. Lakini badala yao unaweza kupata na moja, kwa kutumia utaratibu coalesce ... IN:

coalesce(NEW."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚_"::text, '') IN ('', '"ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚"', '"Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠŸΠΎΠ—Π°Ρ€ΠΏΠ»Π°Ρ‚Π΅"')

Kwa hiyo sisi NULL "tutashika", na ngumu OR Hakuna haja ya kuweka uzio na mabano.

Katika jumla ya

Wacha turekodi kile tulichonacho:

IF (
  coalesce(NEW."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚_"::text, '') IN ('', '"ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚"', '"Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠŸΠΎΠ—Π°Ρ€ΠΏΠ»Π°Ρ‚Π΅"') AND
  (
    OLD."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΠ°ΡˆΠ°ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΡ"
  , OLD."Π£Π΄Π°Π»Π΅Π½"
  , OLD."Π”Π°Ρ‚Π°"
  , OLD."ВрСмя"
  , OLD."Π›ΠΈΡ†ΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
  ) IS DISTINCT FROM (
    NEW."Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΠ°ΡˆΠ°ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΡ"
  , NEW."Π£Π΄Π°Π»Π΅Π½"
  , NEW."Π”Π°Ρ‚Π°"
  , NEW."ВрСмя"
  , NEW."Π›ΠΈΡ†ΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
  )
) THEN ...

Na ukizingatia kuwa kitendaji hiki cha kichochezi kinaweza kutumika tu ndani UPDATE-anzisha kwa sababu ya kupatikana OLD/NEW katika hali ya juu, basi hali hii inaweza kuwekwa ndani WHEN-hali, kama inavyoonyeshwa katika #1...

Chanzo: mapenzi.com

Kuongeza maoni