Come già indicato nei commenti sembra che tu debba aggiornare le tue statistiche.
Il numero stimato di righe che escono dall'unione tra location
ed testruns
è enormemente diverso tra i due piani.
Unire le stime del piano: 1
Stime del piano di query secondarie: 8.748
Il numero effettivo di righe che escono dal join è 14.276.
Ovviamente non ha assolutamente senso intuitivo che la versione di join dovrebbe stimare che dovrebbero provenire 3 righe location
e produrre una singola riga unita mentre la query secondaria stima che una sola di quelle righe produrrà 8.748 dallo stesso join, ma ciò nonostante sono stato in grado per riprodurre questo.
Ciò sembra accadere se non viene incrociato l'istogramma quando vengono create le statistiche. La versione di join assume una sola riga. E la ricerca di uguaglianza singola della query secondaria assume le stesse righe stimate di una ricerca di uguaglianza rispetto a una variabile sconosciuta.
La cardinalità dei testrun è 26244
. Supponendo che sia popolato con tre ID di posizione distinti, la query seguente stima che 8,748
verranno restituite le righe ( 26244/3
)
declare @i int
SELECT *
FROM testruns AS tr
WHERE tr.location_id = @i
Dato che la tabella locations
contiene solo 3 righe, è facile (se non assumiamo chiavi esterne) inventare una situazione in cui vengono create le statistiche e quindi i dati vengono alterati in modo da avere un effetto drammatico sul numero effettivo di righe restituite ma non è sufficiente per attivare l'aggiornamento automatico delle statistiche e ricompilare la soglia.
Man mano che SQL Server rileva il numero di righe che escono da quel join, tutte le stime delle altre righe nel piano di join vengono erroneamente sottovalutate. Oltre a significare che si ottiene un piano seriale, la query ottiene anche una concessione di memoria insufficiente e i tipi e i join hash si riversano a tempdb
.
Di seguito è riportato uno scenario possibile che riproduce le righe effettive rispetto a quelle visualizzate nel piano.
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
Quindi eseguire le seguenti query fornisce la stessa discrepanza stimata rispetto all'effettiva
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' )