2011-12-21 14 views
9

Tengo una vaga memoria de años de trabajo con SQL Server, posiblemente culto a la carga, que cuando tienes una columna posiblemente nula, no es seguro escribir la cláusula "WHERE" predicados como:Operadores lógicos y lógicos y controles nulos

... WHERE the_column IS NULL OR the_column < 10 ... 

tenía algo que ver con el hecho de que las reglas de SQL no estipulan cortocircuitos (y, de hecho, que es una especie de una mala idea, posiblemente por razones de optimización de consultas), y por lo tanto el " < "comparación (o lo que sea) podría evaluarse incluso si el valor de la columna es nulo. Ahora, exactamente por qué eso sería una cosa terrible, no sé, pero yo recuerdo estar severamente advertidos por parte de la documentación de código que siempre como una cláusula "CASE":

... WHERE 1 = CASE WHEN the_column IS NULL THEN 1 WHEN the_column < 10 THEN 1 ELSE 0 END ... 

(el tonto "1 =" parte se debe a que SQL Server no/no tenía booleanos de primera clase, o al menos pensé que no lo hicieron)

Así que mis preguntas aquí son:.

  1. es eso realmente cierto para SQL Server (o tal vez back-rev SQL Server 2000 o 2005) o ¿estoy loco?
  2. Si es así, ¿se aplica la misma advertencia a PostgreSQL? (8.4 si es importante)
  3. ¿Cuál es exactamente el problema? ¿Tiene que ver con cómo funcionan los índices o algo así?

Mi conexión a tierra en SQL es bastante débil.

+1

Quizás estaban hablando de Y? Como nulo Y cualquier cosa es nula, a menudo es necesario unir o un caso donde las expresiones pueden contener términos nulos. –

Respuesta

10

No sé SQL Server, así que no puedo hablar de eso.

Dada una expresión a L b por algún operador lógico L, no hay ninguna garantía de que a serán evaluados antes o después de b o incluso que tanto a y b serán evaluados:

Expression Evaluation Rules

El El orden de evaluación de las subexpresiones no está definido. En particular, las entradas de un operador o función no necesariamente se evalúan de izquierda a derecha o en cualquier otro orden fijo.

Además, si el resultado de una expresión puede determinarse evaluando solo algunas partes de él, entonces otras subexpresiones podrían no evaluarse en absoluto.
[...]
Tenga en cuenta que esto no es lo mismo que el "cortocircuito" de izquierda a derecha de los operadores booleanos que se encuentra en algunos lenguajes de programación.

Como consecuencia, no es prudente utilizar funciones con efectos secundarios como parte de expresiones complejas. Es particularmente peligroso confiar en los efectos secundarios o la orden de evaluación en las cláusulas WHERE y HAVING, ya que esas cláusulas se reprocesan ampliamente como parte del desarrollo de un plan de ejecución.

Por lo que una expresión de la forma:

the_column IS NULL OR the_column < 10 

se refiere, no hay nada de qué preocuparse ya que es NULL < nNULL para todos n, incluso NULL < NULL evalúa a NULL; Por otra parte, NULL no es cierto por lo

null is null or null < 10 

es sólo una manera complicada de decir true or null y eso es true independientemente de la sub-expresión se evalúa primero.

Todo el "use a CASE" suena principalmente como el culto de carga SQL para mí. Sin embargo, como la mayoría del culto a la carga, hay un grano, una verdad enterrada debajo de la carga; justo debajo de mi primer extracto del manual de PostgreSQL, se dará cuenta de esto:

Cuando es esencial para forzar el orden de evaluación, una CASE construcción (véase la Sección 9.16) se puede utilizar. Por ejemplo, esta es una manera poco fiable de tratar de evitar la división por cero en un WHERE cláusula:

SELECT ... WHERE x > 0 AND y/x > 1.5; 

Pero esto es seguro:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END; 

lo tanto, si usted necesita para protegerse de una condición que generará una excepción u otros efectos secundarios, entonces debe usar un CASE para controlar el orden de evaluación como CASE es evaluated in order:

Cada condición es una expresión que arroja un resultado de boolean. Si el resultado de la condición es verdadero, el valor de la expresión CASE es resultado que sigue la condición y el resto de la expresión CASE no se procesa. Si el resultado de la condición no es verdadero, las siguientes cláusulas WHEN se examinan de la misma manera.

Así que dado esto:

case when A then Ra 
    when B then Rb 
    when C then Rc 
    ... 

A se garantiza que sea evaluado antes B, B antes C, etc., y la evaluación se detiene tan pronto como una de las condiciones se evalúa como un valor verdadero.

En resumen, un CASE cortocircuitos ni peros ni ANDOR cortocircuito por lo que sólo necesitan utilizar un CASE cuando es necesario para proteger contra los efectos secundarios.

+1

Sí, gracias; Entiendo el hecho de que SQL no aplica una regla de cortocircuito (o, alternativamente, un "no cortocircuito"). La pregunta realmente es si algo terrible sucede si una comparación relacional ordinaria se evalúa frente a una columna que quizás sea nula. Gracias por la respuesta muy detallada. – Pointy

1

Nunca he oído hablar de un problema así, y this bit of SQL Server 2000 documentation usa WHERE advance < $5000 OR advance IS NULL en un ejemplo, por lo que no debe haber sido una regla muy estricta. Mi única preocupación con OR es que tiene una prioridad menor que AND, por lo que podría escribir accidentalmente algo como WHERE the_column IS NULL OR the_column < 10 AND the_other_column > 20 cuando eso no es lo que quiere decir; pero la solución habitual es paréntesis en lugar de una gran expresión CASE.

Creo que en la mayoría de RDBMS, los índices no incluyen valores nulos, por lo que un índice en the_column no sería terriblemente útil para esta consulta; pero incluso si ese no fuera el caso, no veo por qué una gran expresión CASE sería más fácil de indexar.

(Por supuesto, es difícil probar una negativa, y tal vez otra persona sabrá lo que usted se refiere?)

1

Bueno, repetidamente he escrito consultas como el primer ejemplo desde siempre (por ejemplo, he escrito generadores de consultas que generan consultas como esa), y nunca he tenido un problema.

Creo que puede estar recordando algunas advertencias que alguien le dio alguna vez en contra de escribir funky condiciones de unión que usan OR. En su primer ejemplo, las condiciones unidas por OR restringen la misma columna de la misma tabla, lo cual está bien. Si su segunda condición era una condición de unión (es decir, restringía columnas de dos tablas diferentes), entonces podría entrar en malas situaciones en las que el planificador de consultas no tiene más remedio que usar una combinación cartesiana (¡¡malo, malo, malo !!!)

No creo que su función CASE realmente esté haciendo algo allí, excepto quizás obstaculizar los intentos de su planificador de consultas de encontrar un buen plan de ejecución para la consulta.

Pero de forma más general, solo escriba primero la consulta directa y vea cómo funciona para datos realistas. ¡No hay necesidad de preocuparse por un problema que ni siquiera podría existir!

0

Los nulos pueden ser confusos. El "... WHERE 1 = CASE ..." es útil si está tratando de pasar un Null O un valor como un parámetro ex. "DONDE the_column = @parameter. Este mensaje puede ser útil Passing Null using OLEDB.

1

En lugar de

the_column IS NULL OR the_column < 10 

lo haría

isnull(the_column,0) < 10 

o para el primer ejemplo

WHERE 1 = CASE WHEN isnull(the_column,0) < 10 THEN 1 ELSE 0 END ... 
0

Otro ejemplo donde CASE es útil es cuando se usan funciones de fecha en las columnas varchar. Agregando ISDATE antes que tú cantar decir convertir (colA, datetime) podría no funcionar, y cuando colA tiene datos sin fecha, la consulta puede dar error.

Cuestiones relacionadas