SQL no es ni C++ ni JavaScript. Por lo tanto, la evaluación de las expresiones lógicas se lleva a cabo de manera diferente, y esto no es lo mismo en absoluto:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Durante el proceso de optimización del plan de ejecución de una consulta, PostgreSQL , no calcular algunas de ellas para registros individuales, asignarlas a la condición del índice aplicado… En resumen, es más fácil considerar que no puedes gestionar el orden en que se evaluarán (y si se evaluarán en absoluto) las condiciones equivalentes. Así que si realmente deseas gestionar la prioridad, es necesario estructuralmente hacer que estas condiciones sean desiguales
a través de expresiones condicionales. Los datos y su manejo son la base de nuestro complejo SBIS, y .

Inicial CASE.
#0: RTFM
Por ejemplo, esta forma de evitar la división por cero en la cláusula :
es poco confiable:
SELECT ... WHERE x > 0 AND y/x > 1.5;Una opción segura:WHERESELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;La construcción utilizada de esta maneraprotege la expresión de la optimización, por lo que debe usarse solo cuando sea necesario.
BEGIN IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN ... END IF; RETURN NEW; END;Parece que todo está bien, pero… Nadie garantiza que el bloque anidado
SELECT ... WHERE x > 0 AND y/x > 1.5;no se ejecute cuando la primera condición sea falsa. Lo corregimos utilizando
#1: условие в триггере
IF anidados. BEGIN IF cond(NEW.fld) THEN IF EXISTS(SELECT ...) THEN ... END IF; END IF; RETURN NEW; END; SELECCIONAR Ahora, observemos detenidamente: todo el cuerpo de la función del trigger está "envuelto" en . Lo que significa que nada impide que extraigamos esta condición de la procedura usando WHEN.:
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld); Este enfoque permite garantizar el ahorro de recursos del servidor cuando la condición es falsa. WHEN.SELECT ... WHERE EXISTS(... A) OR EXISTS(... B) :
ambos se ejecutarán.Pero si sabemos que uno de ellos es "verdadero" con mucha más frecuencia (o "falso" para
#2: OR/AND-цепочка
-la cadena), ¿no se puede "elevar su prioridad" para que el segundo no se ejecute innecesariamente? SELECT ... WHERE EXISTS(... A) OR EXISTS(... B) EXISTS En un caso desagradable, podría resultar que ambos sean "verdaderos", pero.
ambos se ejecuten. YPero si sabemos que uno de ellos tiende a ser "verdadero" con mucha más frecuencia (o "falso" en el caso de la cadena) — ¿hay alguna forma de "aumentar su prioridad" para que el segundo no se ejecute innecesariamente?
Resulta que sí — el enfoque algorítmico está cercano al tema del artículo .
Simplemente 'encajemos en CASE' ambas condiciones:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END En este caso no hemos definido ELSE-el valor, es decir, en caso de que ambas condiciones sean falsas SELECT ... WHERE x > 0 AND y/x > 1.5; devolverá NULL, que se interpreta como FALSO en WHERE-condiciones.
Este ejemplo se puede combinar de otra manera — al gusto:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END#3: как [не] надо писать условия
Pasamos dos días analizando las razones del funcionamiento 'extraño' de este disparador — veamos por qué.
Código fuente:
IF( NEW."Documento_" is null or NEW."Documento_" = (select '"Conjunto"'::regclass::oid) or NEW."Documento_" = (select to_regclass('"DocumentoPorSalario"')::oid)
AND ( OLD."DocumentoNuestraOrganizacion" <> NEW."DocumentoNuestraOrganizacion"
OR OLD."Eliminado" <> NEW."Eliminado"
OR OLD."Fecha" <> NEW."Fecha"
OR OLD."Hora" <> NEW."Hora"
OR OLD."Creador" <> NEW."Creador" ) ) THEN ...Problema #1: la desigualdad no considera NULL
Imaginemos que todos OLD-campos tenían valor NULL. ¿Qué pasaría?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL Y desde el punto de vista de la validación de la condición NULL es equivalente FALSO, como se mencionó anteriormente.
Solución: utiliza el operador desde ROW-operador, comparando inmediatamente registros enteros:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUEProblema #2: implementación diferente de la misma funcionalidad
Comparémos:
NEW."Documento_" = (select '"Conjunto"'::regclass::oid)
NEW."Documento_" = (select to_regclass('"DocumentoPorSalario"')::oid) ¿Por qué hay anidamientos innecesarios aquí? SELECCIONAR? А функция to_regclass? А по-разному-то почему?..
Corregir:
NEW."Documento_" = '"Conjunto"'::regclass::oid
NEW."Documento_" = '"DocumentoPorSalario"'::regclass::oidProblema #3: prioridad de las operaciones booleanas
Formateemos el código fuente:
{... IS NULL} OR
{... Conjunto} OR
{... DocumentoPorSalario} AND
( {... desigualdades} ) Ups… De hecho, resultó que si cualquiera de las dos primeras condiciones es verdadera, toda la condición se transforma en VERDADERO, sin considerar desigualdades. Y eso no es en absoluto lo que queríamos.
Corregir:
(
{... IS NULL} OR
{... Conjunto} OR
{... DocumentoPorSalario}
) AND
( {... desigualdades} )Problema #4 (pequeño): una condición OR complicada para un campo
La verdad es que el problema en #3 surgió precisamente porque había tres condiciones. Pero en su lugar, se puede usar una sola, mediante el mecanismo coalesce ... IN:
coalesce(NEW."Documento_"::text, '') IN ('', '"Conjunto"', '"DocumentoPorSalario"') Así que también NULL "capturaremos", y no tendremos que complicar con paréntesis. O no será necesario lidiar con paréntesis.
Total
Fijemos lo que hemos logrado:
IF (
coalesce(NEW."Documento_"::text, '') IN ('', '"Conjunto"', '"DocumentoPorSalario"') AND
(
OLD."DocumentoNuestraOrganizacion"
, OLD."Eliminado"
, OLD."Fecha"
, OLD."Hora"
, OLD."Creador"
) IS DISTINCT FROM (
NEW."DocumentoNuestraOrganizacion"
, NEW."Eliminado"
, NEW."Fecha"
, NEW."Hora"
, NEW."Creador"
)
) THEN ... Y si consideramos que esta función de disparador solo se puede aplicar en ACTUALIZAR-el disparador debido a la presencia de OLD/NEW en la condición de nivel superior, entonces esta condición se puede llevar a En un caso poco agradable, se puede dar que ambos-condición, como se mostró en #1...
Fuente: habr.com
