SQL C++ емес, JavaScript емес. Сондықтан логикалық өрнектерді бағалау әртүрлі және бұл мүлдем бірдей емес:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
PostgreSQL сұрауының орындалу жоспарын оңтайландыру кезінде
Сондықтан, егер сіз әлі де басымдықты басқарғыңыз келсе, құрылымдық түрде қажет бұл шарттарды тең емес етіп жасаңыз шартты түрде
Деректер және олармен жұмыс істеу негізі болып табылады
№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
-тізбектер) - екіншісі тағы бір рет орындалмас үшін, қандай да бір жолмен оның басымдылығын арттыруға болады ма?
Бұл мүмкін екені белгілі болды - алгоритмдік тәсіл мақаланың тақырыбына жақын
Осы екі шарттың екеуін де «CASE астында итерейік»:
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 мәселе: bool басымдылығы
Дереккөзді пішімдейік:
{... 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