Seperti yang sudah ditunjukkan dalam komentar, sepertinya Anda perlu memperbarui statistik Anda.
Perkiraan jumlah baris yang keluar dari gabungan antara location
dan testruns
sangat berbeda antara kedua rencana.
Gabung perkiraan rencana: 1
Perkiraan rencana permintaan sub: 8.748
Jumlah aktual baris yang keluar dari gabungan adalah 14.276.
Tentu saja tidak masuk akal secara intuisi bahwa versi join harus memperkirakan bahwa 3 baris seharusnya berasal location
dan menghasilkan satu baris yang digabungkan sedangkan sub kueri memperkirakan bahwa satu baris tersebut akan menghasilkan 8.748 dari gabungan yang sama tetapi saya tetap bisa untuk mereproduksi ini.
Ini tampaknya terjadi jika tidak ada persilangan di antara histogram ketika statistik dibuat. Versi bergabung mengasumsikan satu baris. Dan pencarian kesetaraan tunggal dari sub kueri mengasumsikan baris perkiraan yang sama dengan pencarian kesetaraan terhadap variabel yang tidak diketahui.
Kardinalitas testruns adalah 26244
. Dengan asumsi bahwa diisi dengan tiga id lokasi yang berbeda maka perkiraan permintaan berikut ini bahwa 8,748
baris akan dikembalikan ( 26244/3
)
declare @i int
SELECT *
FROM testruns AS tr
WHERE tr.location_id = @i
Mengingat bahwa tabel locations
hanya berisi 3 baris itu mudah (jika kita menganggap tidak ada kunci asing) untuk membuat situasi di mana statistik dibuat dan kemudian data diubah dengan cara yang secara dramatis mempengaruhi jumlah baris yang dikembalikan tetapi tidak cukup untuk trip pembaruan statistik otomatis dan kompilasi ulang ambang batas.
Ketika SQL Server mendapatkan jumlah baris yang keluar dari join tersebut, maka salah semua estimasi baris lain dalam rencana join secara besar-besaran diremehkan. Selain berarti bahwa Anda mendapatkan paket serial, kueri juga mendapat hibah memori yang tidak mencukupi dan jenis serta hash yang bergabung tempdb
.
Salah satu skenario yang mungkin mereproduksi baris yang sebenarnya vs estimasi yang ditunjukkan dalam paket Anda adalah di bawah ini.
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
Kemudian menjalankan kueri berikut memberikan estimasi perbedaan yang sama vs aktual
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' )