Como ya se indicó en los comentarios, parece que necesita actualizar sus estadísticas.
El número estimado de filas que salen de la unión entre location
y testruns
es muy diferente entre los dos planes.
Unirse a las estimaciones del plan: 1
Estimaciones del plan de subconsulta: 8.748
El número real de filas que salen de la unión es 14,276.
Por supuesto, no tiene ningún sentido intuitivo que la versión de unión deba estimar que 3 filas deberían provenir location
y producir una sola fila unida, mientras que la subconsulta estima que una sola de esas filas producirá 8.748 de la misma unión, pero de todos modos pude para reproducir esto
Esto parece suceder si no hay cruce entre los histogramas cuando se crean las estadísticas. La versión de unión supone una sola fila. Y la búsqueda de igualdad única de la subconsulta supone las mismas filas estimadas que una búsqueda de igualdad contra una variable desconocida.
La cardinalidad de testruns es 26244
. Suponiendo que se rellena con tres identificadores de ubicación distintos, la siguiente consulta estima que 8,748
se devolverán las filas ( 26244/3
)
declare @i int
SELECT *
FROM testruns AS tr
WHERE tr.location_id = @i
Dado que la tabla locations
solo contiene 3 filas, es fácil (si suponemos que no hay claves externas) idear una situación en la que se crean las estadísticas y luego los datos se modifican de una manera que afecta drásticamente el número real de filas devueltas pero es insuficiente para desconecte la actualización automática de estadísticas y vuelva a compilar el umbral.
A medida que SQL Server obtiene el número de filas que salen de esa unión tan mal, todas las otras estimaciones de filas en el plan de unión se subestiman enormemente. Además de significar que obtiene un plan en serie, la consulta también obtiene una concesión de memoria insuficiente y las clasificaciones y las combinaciones hash se derraman tempdb
.
A continuación se muestra un posible escenario que reproduce las filas reales frente a las estimadas que se muestran en su plan.
CREATE TABLE location
(
id INT CONSTRAINT locationpk PRIMARY KEY,
location VARCHAR(MAX) /*From the separate filter think you are using max?*/
)
/*Temporary ids these will be updated later*/
INSERT INTO location
VALUES (101, 'Coventry'),
(102, 'Nottingham'),
(103, 'Derby')
CREATE TABLE testruns
(
location_id INT
)
CREATE CLUSTERED INDEX IX ON testruns(location_id)
/*Add in 26244 rows of data split over three ids*/
INSERT INTO testruns
SELECT TOP (5984) 1
FROM master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (5984) 2
FROM master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (14276) 3
FROM master..spt_values v1, master..spt_values v2
/*Create statistics. The location_id histograms don't intersect at all*/
UPDATE STATISTICS location(locationpk) WITH FULLSCAN;
UPDATE STATISTICS testruns(IX) WITH FULLSCAN;
/* UPDATE location.id. Three row update is below recompile threshold*/
UPDATE location
SET id = id - 100
Luego, ejecutar las siguientes consultas da la misma discrepancia estimada vs real
SELECT *
FROM testruns AS tr
WHERE tr.location_id = (SELECT id
FROM location
WHERE location = 'Derby')
SELECT *
FROM testruns AS tr
JOIN location loc
ON tr.location_id = loc.id
WHERE loc.location = ( 'Derby' )