7

Tengo tres tablas:Método abreviado de ejecución SQL Query O lógica?

SmallTable 
    (id int, flag1 bit, flag2 bit) 
JoinTable 
    (SmallTableID int, BigTableID int) 
BigTable 
    (id int, text1 nvarchar(100), otherstuff...) 

SmallTable tiene, a lo sumo, una docena de pocos registros. BigTable tiene algunos millones, y en realidad es una vista que SINDICA una tabla en esta base de datos con una tabla en otra base de datos en el mismo servidor.

Aquí es unirse a la lógica:

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=1 OR b.text1 <> 'value1') 

media unió tamaño es unos pocos miles de resultados. Todo lo que se muestra está indexado.

Para la mayoría de los registros SmallTable, flag1 y flag2 se ponen a 1, por lo que no hay realmente ninguna necesidad incluso de acceder al índice de BigTable.text1, pero SQL Server de todos modos, lo que lleva a una exploración indexado costoso y bucle anidado.

¿Hay una mejor manera de hacer alusión a SQL Server que, si flag1 y flag2 se establecen en 1, debería ni siquiera se molestó en mirar a text1?

En realidad, si puedo evitar la unión a BigTable por completo en estos casos (joinTable se gestiona, así que esto no crearía un problema), que haría esta consulta clave aún más rápido.

+0

+1 para la pregunta interesante. ¡Espero aprender más de esto yo mismo! – AdaTheDev

+0

Mencionó un análisis de índice en 'BigTable', que es una vista. ¿Es una vista indexada, o la exploración de índice se realiza en las tablas subyacentes? ¿Podría publicar el plan de consulta aquí? – Quassnoi

Respuesta

5

SQL La evaluación booleana hace NO garantiza el cortocircuito del operador. Consulte On SQL Server boolean operator short-circuit para obtener un ejemplo claro que muestra cómo asumir el cortocircuito del operador puede conducir a problemas de corrección y errores de tiempo de ejecución.

Por otro lado, el mismo ejemplo en mi enlace muestra que funciona para SQL Server: proporciona una ruta de acceso que SQL puede usar. Por lo tanto, al igual que con todos Problemas y preguntas sobre el rendimiento SQL, el verdadero problema no está en la forma en que se expresa el texto SQL, sino en el diseño de su almacenamiento. Es decir. ¿Qué índices tiene el optimizador de consultas a su disposición para satisfacer su consulta?

+0

Estoy de acuerdo: este no es un problema de corta duración per se, pero un problema donde el motor de consulta envía el trabajo para verificar el valor de 'text1' incluso con ambas banderas se establece en 1. Es una operación indexada, pero innecesario * si * todos los registros seleccionados en SmallTable tienen esos indicadores establecidos. Como comenté en otro lugar, creo que es un problema con el optimizador de consultas que no es "lo suficientemente inteligente" como para evitar la rama de trabajo en BigTable.text1 donde todos los registros en SmallTable no requieren las comparaciones de texto. – richardtallent

+1

Desafortunadamente no hay operadores de árbol de consultas condicionales/condicionales. En otras palabras, no hay operadores que digan 'si la condición es verdadera, sigan por este camino, sino en este otro camino'. La consulta optimizare tiene que crear un plan que satisfaga todas las condiciones posibles, * incluso aquellas que tienen una probabilidad muy baja *. El plan de consulta puede hacer muchos trucos cuando hay condiciones * deterministas *, ej. dos tablas en una unión con una relación extranjera de confianza * pueden * eliminar por completo una tabla del plan. Aquí es donde todos los trucos de T-SQL entran en escena, como usar UNION en lugar de OR. –

+0

Ese es un muy buen comentario. Estaba luchando por decir mi entendimiento, pero este mi humilde opinión es una gran explicación. – AdaTheDev

0

Ni idea de si esto va a ser más rápido y sin datos de prueba ... pero suena como que podría

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1) AND (s.flag2=1) 
UNION ALL 
SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=0 AND b.text1 <> 'value1') 

me dejó saber lo que sucede

También, usted podría ser capaz de acelerar esto simplemente devolviendo solo una identificación única para esta consulta y luego usando el resultado de eso para obtener el resto de los datos.

edición

algo como esto?

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1) AND (s.flag2=1) 
UNION ALL 
SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE EXISTS 
    (SELECT 1 from BigTable b 
    WHERE 
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=0 AND b.text1 <> 'value1') 
) 
+0

Intenté esto, en realidad fue mucho más lento cuando cualquiera de las banderas se estableció en 1. – richardtallent

+0

Creo que hay una manera de hacer esto con uniones extra y una orden de fuerza, pero mi cerebro es confuso de pensarlo ahora. – Hogan

0

No es elegante, pero debería funcionar ...

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1 = 1 and s.flag2 = 1) OR 
    (
     (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%') 
     AND (s.flag2=1 OR b.text1 <> 'value1') 
    ) 
+0

Gracias ... intenté esto, pero la ejecución fue idéntica a lo que pude ver. – richardtallent

1

no creo SQL Server condiciones de corto circuito como el que por desgracia.

SO Yo sugeriría hacer 2 consultas y UNIONARlas juntas. Primera consulta con s.flag1 = 1 y s.flag2 = 1 DONDE las condiciones, y la segunda consulta realizando la unión a BigTable con el s.flag1 <> 1 a s.flag2 <> 1 condiciones.

This artículo sobre el asunto es digno de una lectura, e incluye la línea de fondo:

... SQL Server no hace cortocircuitando como se hace en otros lenguajes de programación y hay nada que pueda hacer para forzarlo a.

Actualización:
This artículo es también una lectura interesante y contiene algunos buenos enlaces sobre este tema, incluyendo un chat de TechNet con el gerente de desarrollo para el equipo procesador de consultas de SQL Server que menciona brevemente que el optimizador hace permitir la evaluación de cortocircuito. La impresión general que recibo de varios artículos es "sí, el optimizador puede detectar la oportunidad de cortocircuito, pero no debe confiar en ello y no puede forzarlo". Por lo tanto, creo que el enfoque de UNION puede ser tu mejor opción. Si no se llega a un plan que aproveche la oportunidad de atajo, eso se reduciría al optimizador basado en costos pensando que se encontró un plan razonable que no lo hace (esto se reduciría a índices, estadísticas, etc.) .

+1

Buen comentario, pero mientras SQL Server no abrevia la evaluación de expresiones, * does * do query optimization. Por lo tanto, si mi consulta está limitada a un conjunto de resultados de SmallTable donde ambos indicadores son '1', debería ser lo suficientemente inteligente para omitir la comprobación de' BigTable.text1', pero eso requeriría cambiar el plan de ejecución en función de los datos. Creo que ese es el problema central. – richardtallent

+0

Sí, veo lo que está diciendo, pero todavía lo veo como un atajo: el optimizador debería ser capaz de ver la oportunidad de un cortocircuito. He hecho algunas pruebas yo mismo en el pasado sobre este tipo de cosas, y eso es lo que he visto (no hay optimización de esta manera). – AdaTheDev

+0

El problema es que para un 'o' se puede evaluar en cualquier orden y está haciendo primero el lado derecho. – Hogan

0

SQL Server normalmente agarra la pista subconsulta (aunque está libre para descartarlo):

SELECT  * 
FROM  (
      SELECT * FROM SmallTable where flag1 <> 1 or flag2 <> 1 
      ) s 
INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
...