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.
¿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? –
@Martin "Tengo un software que usa la consulta 2, y no puedo cambiar eso" –
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. –