Mengapa subquery menggunakan paralelisme dan bergabung tidak?

16

Mengapa SQL server menggunakan paralelisme saat menjalankan kueri ini yang menggunakan subquery tetapi tidak ketika menggunakan gabungan? Versi gabungan berjalan dalam serial dan membutuhkan waktu sekitar 30 kali lebih lama untuk diselesaikan.

Bergabung dengan versi: ~ 30secs

masukkan deskripsi gambar di sini

Versi subquery: <1detik

masukkan deskripsi gambar di sini

EDIT: Versi rencana permintaan xml:

Versi BERGABUNG

Versi SUBQUERY

Chris L
sumber

Jawaban:

12

Seperti yang sudah ditunjukkan dalam komentar, sepertinya Anda perlu memperbarui statistik Anda.

Perkiraan jumlah baris yang keluar dari gabungan antara locationdan testrunssangat berbeda antara kedua rencana.

Gabung perkiraan rencana: 1

Paket 1

Perkiraan rencana permintaan sub: 8.748

masukkan deskripsi gambar di sini

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 locationdan 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,748baris akan dikembalikan ( 26244/3)

declare @i int

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = @i

Mengingat bahwa tabel locationshanya 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' ) 
Martin Smith
sumber
Jika batasan unik ditambahkan pada lokasi maka menjadi jelas bahwa "=" akan mengembalikan tepat satu baris. Kemudian dalam contoh Anda, rencana kueri menjadi identik (pindai -> mencari): ubah tabel Lokasi tambahkan kendala U_Location_Location unik nonclustered (Lokasi);
crokusek
@crokusek ya. Menyadari apa yang Anda maksud setelah itu dan menghapus komentar saya! Apakah itu meningkatkan perkiraan jumlah baris untuk versi join agar sama dengan subquery juga? Tidak pada PC saat ini untuk menguji?
Martin Smith
@crokusek Yap. tampak seperti baris taksiran yang sama dari gabungan seperti untuk sub kueri dalam kasus tunggal.
Martin Smith
Iya. Rencana permintaan yang identik, keduanya memperkirakan 8748, keduanya aktual 14276. Btw, saya pikir pra-komputasi locationId akan menyelesaikan perbedaan itu tetapi tidak.
crokusek
1
@crokusek - Saya juga akan menambahkan batasan unik ke lokasi dan tempat lain yang serupa dalam DB saya. Saya harus mengakui bahwa saya tidak menyadarinya memengaruhi optimisasi kueri. Saya pikir itu hanya untuk memastikan integritas data. Terima kasih atas masukan Anda untuk pertanyaan ini.
Chris L