8

Uso Microsoft SQL Server 2008 (SP1, x64). Tengo dos consultas que hacen lo mismo, o eso creo, pero tienen planes de consulta y rendimiento completamente diferentes.¿Cuál es la diferencia entre estas consultas T-SQL que utilizan OR?

Consulta 1:

SELECT c_pk 
FROM table_c 
WHERE c_b_id IN (SELECT b_id FROM table_b WHERE b_z = 1) 
    OR c_a_id IN (SELECT a_id FROM table_a WHERE a_z = 1) 

Consulta 2:

SELECT c_pk 
FROM table_c 
LEFT JOIN (SELECT b_id FROM table_b WHERE b_z = 1) AS b ON c_b_id = b_id 
LEFT JOIN (SELECT a_id FROM table_a WHERE a_z = 1) AS a ON c_a_id = a_id 
WHERE b_id IS NOT NULL 
    OR a_id IS NOT NULL 

consulta 1 es rápido como me esperaba, mientras que consulta 2 es muy lento. El query plans se ve bastante diferente.

Me gustaría que la consulta 2 sea tan rápida como la consulta 1. Tengo un software que usa la consulta 2, y no puedo cambiar eso a la consulta 1. Puedo cambiar la base de datos.

Algunas preguntas:

  • por qué son los planes de consulta diferente?
  • ¿Puedo "enseñar" a SQL Server de alguna manera que la consulta 2 es igual a la consulta 1?

Todas las tablas han (agrupado) claves primarias e índices adecuados en todas las columnas:

CREATE TABLE table_a (
    a_pk int NOT NULL PRIMARY KEY, 
    a_id int NOT NULL UNIQUE, 
    a_z int 
) 
GO 
CREATE INDEX IX_table_a_z ON table_a (a_z) 
GO 

CREATE TABLE table_b (
    b_pk int NOT NULL PRIMARY KEY, 
    b_id int NOT NULL UNIQUE, 
    b_z int 
) 
GO 
CREATE INDEX IX_table_b_z ON table_b (b_z) 
GO 

CREATE TABLE table_c (
    c_pk int NOT NULL PRIMARY KEY, 
    c_a_id int, 
    c_b_id int 
) 
GO 
CREATE INDEX IX_table_c_a_id ON table_c (c_a_id) 
GO 
CREATE INDEX IX_table_c_b_id ON table_c (c_b_id) 
GO 

Las mesas no son modificados después de llenar inicialmente. Soy el único que los está interrogando. Contienen millones de registros (table_a: 5M, table_b: 4M, table_c: 12M), pero usar solo el 1% da resultados similares.

Edición: He intentado añadir claves externas para c_a_id y c_b_id, pero eso sólo hizo más lenta consulta 1 ...

espero que alguien pueda echar un vistazo a la query plans y explicar la diferencia.

+0

¿Cuál es la motivación para esto? 'IN/EXISTS' es generalmente más eficiente que' OUTER JOIN ... NULL' en SQL Server y la primera consulta me parece más clara, ¿por qué no utilizar la primera? –

+2

@Martin "Tengo un software que usa la consulta 2, y no puedo cambiar eso" –

+0

En general, las consultas no son las mismas ya que la unión puede generar filas duplicadas, mientras que la unión semiactiva no lo hace. Aunque no he comprobado si tienes alguna restricción que impida esto todavía. –

Respuesta

1

Únete son más lentos, déjenme decir por diseño. La primera consulta utiliza una subconsulta (almacenable en caché) para filtrar registros, por lo que generará menos datos (y menos accesos a cada tabla).

¿Leyó usted los siguientes:

Lo que quiero decir es que con en el PP se puede hacer mejor optimizaciones como la eliminación de duplicados, se detienen en el primer partido y similar (y estos son de escuela recuerdos, así que estoy seguro de que va a hacer mucho mejor). Entonces, , supongo, la pregunta no es por qué QP es diferente, pero qué tan inteligentes pueden ser las optimizaciones.

+1

'IN' es una semi unión. No estoy seguro de lo que quiere decir con sub consulta cacheable. –

+0

SQL Server es bastante bueno en la optimización de JOINs y subconsultas, y usará la búsqueda más rápida. Pero no en este caso. Entiendo los índices, no creo que tu enlace agregue nada relevante. –

+0

Agregué alguna explicación de lo que quiero decir –

0

Estás comparando consultas no equivalentes y estás usando left join de una manera bastante inusual. Generalmente si el suyo intención era seleccionar todas las entradas en table_c que ha vinculadas documentación, en TABLE_A o table_b que puedes usar existe declaración:

SELECT c_pk 
FROM table_c 
WHERE Exists( 
SELECT 1 
FROM table_b 
WHERE b_z = 1 and c_b_id = b_id 
) OR Exists( 
SELECT 1 
FROM table_a 
WHERE a_z = 1 and c_a_id = a_id 
) 
+1

Si publica código, XML o muestras de datos, ** FAVOR ** resalte esas líneas en el editor de texto y haga clic en el botón "muestras de código" ('{}') en la barra de herramientas del editor Para formatear y sintaxis, ¡destaquelo! –

0

Puesto que no se puede cambiar la consulta, al menos se puede mejorar la consulta de ambiente.

  1. resaltar su consulta, haga clic en él en SSMS y seleccione "Analizar consulta en DTA."
  2. Ejecute el análisis para averiguar si necesita índices adicionales o estadísticas creadas.
  3. Consejo de Heed SQL Server.
+0

No veo ningún "Asesor de ajuste" en mi SSMS. El plan de ejecución estimado no mostró ningún índice faltante. Todas las columnas ya están indexadas, ¿qué crees que hay para agregar? –

+1

@MicheldeRuiter - Duda de que haya algo que pueda agregar. Tendrá que volver a escribir la consulta o vivir con el rendimiento. SQL Server no parece capaz de convertir 'OR' a 'UNION' en este caso, por lo que procesa todas las filas en 'table_c' outer joined en las otras dos tablas y luego hace el filtro al final. –

+1

Probablemente tengas la versión gratuita donde esto no está disponible o no la instalaste. – JeffO

Cuestiones relacionadas