SQL은 C++도 아니고 JavaScript도 아닙니다. 따라서 논리식 계산은 다르게 발생하며 이는 전혀 동일하지 않습니다.
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
PostgreSQL 쿼리 실행 계획을 최적화하는 중
그러므로 그래도 우선순위를 관리하고 싶다면 구조화해야 합니다. 이러한 조건을 불평등하게 만들어라 조건문 사용
데이터와 그에 따른 작업이 기본입니다
#0: RTFM
시작
평가 순서가 중요한 경우 구문을 사용하여 캡처할 수 있습니다.
CASE
. 예를 들어, 이는 문장에서 XNUMX으로 나누는 것을 방지하는 방법입니다.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);
이 접근 방식은 조건이 false인 경우 서버 리소스를 절약하는 것이 보장됩니다.
#2: OR/AND 체인
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(소형): 한 필드에 대한 복잡한 OR 조건
사실 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에 표시된 대로...
출처 : habr.com