SQL no es C++, ni es JavaScript. Por lo tanto, la evaluación de las expresiones lógicas es diferente, y esto no es lo mismo en absoluto:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Al optimizar el plan de ejecución de una consulta PostgreSQL
Por lo tanto, si aún desea administrar la prioridad, necesita estructuralmente hacer que estas condiciones sean desiguales con condicional
Los datos y trabajar con ellos es la base
#0: RFM
Comenzando
Cuando el orden de evaluación es importante, se puede arreglar con el constructo
CASE
. Por ejemplo, de esta manera para evitar la división por cero en una oraciónWHERE
faltón:SELECT ... WHERE x > 0 AND y/x > 1.5;
Opción segura:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
La construcción utilizada
CASE
protege la expresión de la optimización, por lo que solo debe usarse cuando sea necesario.
#1: condición de activación
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Todo parece ir bien, pero... Nadie promete que lo invertido SELECT
no se ejecutará si la primera condición es falsa. arreglarlo con anidado IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Ahora echemos un vistazo detenidamente: todo el cuerpo de la función de activación resultó estar "envuelto" en IF
. Y esto significa que nada nos impide eliminar esta condición del procedimiento usando WHEN
-condiciones
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Este enfoque le permite ahorrar recursos del servidor con una garantía si la condición es falsa.
#2: cadena O/Y
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
De lo contrario, se puede obtener que ambos EXISTS
será cierto, pero ambos serán ejecutados.
Pero si sabemos con certeza que uno de ellos es "verdadero" con mucha más frecuencia (o "falso" - por AND
-cadenas) - ¿es posible de alguna manera "aumentar su prioridad" para que el segundo no se ejecute una vez más?
Resulta que es posible: el enfoque algorítmico está cerca del tema del artículo.
Simplemente "empujemos bajo CASE" estas dos condiciones:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
En este caso, no definimos ELSE
-valor, es decir, si ambas condiciones son falsas CASE
volverá NULL
, que se interpreta como FALSE
в WHERE
- condiciones.
Este ejemplo se puede combinar de otra manera, al gusto y color:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: cómo [no] escribir condiciones
Pasamos dos días analizando las razones de la activación "extraña" de este desencadenante; veamos por qué.
Fuente:
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 ...
Problema n.º 1: la desigualdad no tiene en cuenta NULL
Supongamos que todo OLD
-campos importados NULL
. ¿Lo que sucederá?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Y desde el punto de vista de la elaboración de las condiciones NULL
equivalente FALSE
, como se ha mencionado más arriba.
Solución: usar operador IS DISTINCT FROM
ROW
-operador, comparando registros completos a la vez:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Problema número 2: implementación diferente de la misma funcionalidad
Comparar
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
¿Por qué hay inversiones extra? SELECT
? Una función to_regclass
? ¿Por qué es diferente...
Arreglemos:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
Problema #3: precedencia bool
Formateemos la fuente:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
Vaya ... De hecho, resultó que en el caso de la verdad de cualquiera de las dos primeras condiciones, toda la condición se convierte en TRUE
, sin tener en cuenta las desigualdades. Y esto no es en absoluto lo que queríamos.
Arreglemos:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
Problema #4 (pequeño): condición OR compleja para un campo
En realidad, tuvimos problemas en el No. 3 precisamente porque había tres condiciones. Pero en lugar de ellos, puedes arreglártelas con uno, usando el mecanismo coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
Así somos nosotros NULL
"atrapar" y complejo OR
No tienes que preocuparte por los paréntesis.
En total
Arreglemos lo que tenemos:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
Y dado que esta función de disparo solo se puede utilizar en UPDATE
desencadenar debido a la presencia OLD/NEW
en la condición de nivel superior, entonces esta condición generalmente se puede sacar en WHEN
-condición como se muestra en el #1...
Fuente: habr.com