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 (சிறியது): ஒரு புலத்திற்கான சிக்கலான அல்லது நிபந்தனை

உண்மையில், மூன்று நிபந்தனைகள் இருந்ததால் துல்லியமாக எண். 3 இல் எங்களுக்கு சிக்கல்கள் இருந்தன. ஆனால் அவர்களுக்கு பதிலாக, நீங்கள் பொறிமுறையைப் பயன்படுத்தி ஒன்றைப் பெறலாம் 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

கருத்தைச் சேர்