PostgreSQL ضد نمونې: په SQL کې د حالت ارزونه

SQL نه C++ دی او نه دا جاواسکریپټ دی. له همدې امله، د منطقي څرګندونو ارزونه توپیر لري، او دا په اصل کې ورته شی نه دی:

WHERE fncondX() AND fncondY()

= fncondX() && fncondY()

پداسې حال کې چې د PostgreSQL پوښتنې د اجرا کولو پلان اصلاح کول کولی شي په خپله خوښه د مساوي شرایطو "بیا تنظیم" کړيد انفرادي ریکارډونو لپاره د دوی هیڅ حساب مه کوئ، د پلي شوي شاخص حالت ته مراجعه وکړئ ... په لنډه توګه، ترټولو اسانه لاره دا ده چې فرض کړئ چې تاسو اداره نشي کولی هغه ترتیب چې دوی به وي (او ایا دوی به په بشپړ ډول حساب شي) مساوي شرایط

له همدې امله، که تاسو اوس هم غواړئ لومړیتوب اداره کړئ، تاسو اړتیا لرئ چې جوړښتي بڼه ولرئ دا شرایط غیر مساوي کړئ په مشروط ډول څرګندونې и چلونکي.

PostgreSQL ضد نمونې: په SQL کې د حالت ارزونه
ډاټا او د دوی سره کار کول اساس دی زموږ د VLSI کمپلیکس څخه، نو دا زموږ لپاره خورا مهم دی چې په دوی باندې عملیات نه یوازې په سمه توګه ترسره کیږي ، بلکه په مؤثره توګه هم. راځئ کنکریټ مثالونه وګورو چیرې چې د بیان ارزونې کې غلطۍ رامینځته کیدی شي ، او چیرې دا د دوی موثریت ښه کولو ارزښت لري.

#0: RTFM

پیل کول مثال د اسنادو څخه:

کله چې د ارزونې ترتیب مهم وي، دا د ساختمان سره تنظیم کیدی شي CASE. د مثال په توګه، دا لاره په یوه جمله کې د صفر لخوا ویشلو څخه مخنیوی کوي WHERE بې اعتباره:

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

خوندي انتخاب:

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

ساختمان کارول شوی CASE بیان د اصلاح کولو څخه ساتي، نو دا باید یوازې د اړتیا په وخت کې وکارول شي.

#1: د محرک حالت

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

هرڅه ښه ښکاري، مګر ... هیڅوک ژمنه نه کوي چې پانګه اچونه یې کړې SELECT نه به اجرا شي که لومړی شرط غلط وي. سره سم کړئ ځالې IF:

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

اوس راځئ چې په دقت سره وګورو - د ټریګر فنکشن ټول بدن "لپټ" شو IF. او دا پدې مانا ده چې هیڅ شی موږ ته د دې حالت د کارولو له پروسې څخه د لرې کولو مخه نه نیسي WHEN- شرایط:

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

دا طریقه تاسو ته اجازه درکوي د تضمین سره د سرور سرچینې خوندي کړئ که حالت غلط وي.

#2: یا/او سلسله

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

که نه نو، دا ترلاسه کیدی شي چې دواړه EXISTS ریښتیا به وي، مګر دواړه به اعدام شي.

مګر که موږ په یقین سره پوهیږو چې یو له دوی څخه "ریښتیا" دی ډیر ځله (یا "غلط" - لپاره ANDزنځیرونه) - ایا دا امکان لري چې په یو ډول "خپل لومړیتوب ډیر کړي" ترڅو دوهم یو ځل بیا اعدام نشي؟

دا معلومه شوه چې دا ممکنه ده - د الګوریتمیک طریقه د مقالې موضوع ته نږدې ده PostgreSQL انټي پیټرنز: نادره ننوتل د یوځای کیدو مینځ ته رسیږي.

راځئ چې یوازې دا دواړه شرایط "د قضیې لاندې وغورځوو":

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

په دې حالت کې، موږ تعریف نه شو ELSE- ارزښت، دا دی، که دواړه شرایط غلط وي CASE بیرته به راشي NULL، کوم چې په توګه تشریح کیږي FALSE в WHERE- شرایط.

دا مثال په بل ډول سره یوځای کیدی شي - د خوند او رنګ لپاره:

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

#3: د شرایطو لیکلو څرنګوالی

موږ دوه ورځې د دې محرک د "عجیب" محرک لاملونو تحلیل کولو کې تیرې کړې - راځئ چې وګورو ولې.

سرچینه:

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: نابرابري د NULL لپاره حساب نه کوي

راځئ چې دا هر څه فرض کړو OLD- ساحې مهمې دي NULL. څه به وشي؟

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

او د شرایطو څخه د کار کولو له نظره NULL برابر FALSEلکه څنګه چې پورته یادونه وشوه.

پریکړه: چلونکی وکاروئ IS DISTINCT FROM от ROW- آپریټر، په یوځل کې ټول ریکارډونه پرتله کول:

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

ستونزه 2: د ورته فعالیت مختلف پلي کول

پرتله کول:

NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)

ولې اضافي پانګونې شتون لري SELECT؟ یو فعالیت to_regclass؟ ولې توپیر لري ...

راځئ چې اصلاح کړو:

NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid

ستونزه #3: د بول لومړیتوب

راځئ چې سرچینه فارمیټ کړو:

{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )

او په حقیقت کې، دا معلومه شوه چې د لومړیو دوو حالتونو څخه د حقیقت په صورت کې، ټول حالت بدلیږي. TRUEد نابرابریو په پام کې نیولو سره. او دا هغه څه ندي چې موږ یې غوښتل.

راځئ چې اصلاح کړو:

(
  {... IS NULL} OR
  {... Комплект} OR
  {... ДокументПоЗарплате}
) AND
( {... неравенства} )

ستونزه #4 (کوچنۍ): د یوې ساحې لپاره پیچلې یا حالت

په حقیقت کې موږ په دریم نمبر کې ستونزې درلودې ځکه چې درې شرایط شتون درلود. مګر د دوی پرځای ، تاسو کولی شئ د میکانیزم په کارولو سره د یو سره ترلاسه کړئ coalesce ... IN:

coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')

همداسې موږ یو NULL "کیچ"، او پیچلي OR تاسو اړتیا نلرئ د قوسونو سره ګډوډي وکړئ.

ټول

راځئ چې هغه څه سم کړو چې موږ ترلاسه کړل:

IF (
  coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
  (
    OLD."ДокументНашаОрганизация"
  , OLD."Удален"
  , OLD."Дата"
  , OLD."Время"
  , OLD."ЛицоСоздал"
  ) IS DISTINCT FROM (
    NEW."ДокументНашаОрганизация"
  , NEW."Удален"
  , NEW."Дата"
  , NEW."Время"
  , NEW."ЛицоСоздал"
  )
) THEN ...

او ورکړل شوی چې دا محرک فنکشن یوازې په کې کارول کیدی شي UPDATEد شتون له امله محرک OLD/NEW د پورتنۍ کچې حالت کې، نو دا حالت په عمومي توګه بهر اخیستل کیدی شي WHEN- حالت لکه څنګه چې په # 1 کې ښودل شوی ...

سرچینه: www.habr.com

Add a comment