СКЛ није Ц++, нити ЈаваСцрипт. Дакле, процена логичких израза је другачија, а ово уопште није иста ствар:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Док оптимизујете план извршења ПостгреСКЛ упита
Стога, ако и даље желите да управљате приоритетом, морате структурално чине ове услове неједнаким са условним
Подаци и рад са њима су основа
#0: РТФМ
Почиње
Када је редослед вредновања важан, он се може фиксирати конструктом
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
-ланци) - да ли је могуће некако "повећати његов приоритет" да се други не изврши још једном?
Испоставља се да је могуће - алгоритамски приступ је близак теми чланка
Хајде да само "гурнемо под ЦАСЕ" оба ова услова:
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: Неједнакост не узима у обзир НУЛЛ
Претпоставимо да је све 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...
Извор: ввв.хабр.цом