Como já indicado nos comentários, parece que você precisa atualizar suas estatísticas.
O número estimado de linhas saindo da junção entre location
e testruns
é extremamente diferente entre os dois planos.
Estimativas do plano de participação: 1
Estimativas do plano de subconsulta: 8.748
O número real de linhas saindo da associação é 14.276.
É claro que não faz absolutamente nenhum sentido intuitivo que a versão de junção calcule que três linhas devem surgir location
e produza uma única linha de junção, enquanto a subconsulta estima que uma única dessas linhas produzirá 8.748 da mesma junção, mas mesmo assim eu fui capaz para reproduzir isso.
Isso parece acontecer se não houver cruzamento entre os histogramas quando as estatísticas são criadas. A versão de junção assume uma única linha. E a busca de igualdade única da subconsulta assume as mesmas linhas estimadas que uma busca de igualdade em relação a uma variável desconhecida.
A cardinalidade dos testruns é 26244
. Supondo que seja preenchido com três IDs de local distintos, a consulta a seguir estima que as 8,748
linhas serão retornadas ( 26244/3
)
declare @i int
SELECT *
FROM testruns AS tr
WHERE tr.location_id = @i
Dado que a tabela locations
contém apenas três linhas, é fácil (se não assumirmos chaves estrangeiras) inventar uma situação em que as estatísticas são criadas e os dados são alterados de uma maneira que afeta drasticamente o número real de linhas retornadas, mas é insuficiente para defina a atualização automática de estatísticas e recompile o limite.
À medida que o SQL Server obtém o número de linhas que saem dessa associação tão errado, todas as outras estimativas de linha no plano de associação são subestimadas em massa. Assim como significa que você obtém um plano serial, a consulta também recebe uma concessão de memória insuficiente e as classificações e hash se espalham tempdb
.
Um cenário possível que reproduz as linhas reais vs estimadas mostradas no seu plano está abaixo.
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
A execução das consultas a seguir fornece a mesma discrepância 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' )