Saya mencoba untuk menghasilkan contoh rencana kueri untuk menunjukkan mengapa UNION menggunakan dua set hasil dapat lebih baik daripada menggunakan ATAU dalam klausa GABUNG. Paket permintaan yang saya tulis membuat saya bingung. Saya menggunakan database StackOverflow dengan indeks nonclustered pada Users.Reputation.
CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Rencana kueri ada di https://www.brentozar.com/pastetheplan/?id=BkpZU1MZE , durasi kueri untuk saya adalah 4:37 menit, 26612 baris dikembalikan.
Saya belum pernah melihat gaya pemindaian konstan ini dibuat dari tabel yang ada sebelumnya - Saya tidak terbiasa dengan mengapa ada pemindaian konstan dijalankan untuk setiap baris, ketika pemindaian konstan biasanya digunakan untuk satu baris yang dimasukkan oleh pengguna. misalnya SELECT GETDATE (). Mengapa ini digunakan di sini? Saya akan sangat menghargai beberapa panduan dalam membaca rencana permintaan ini.
Jika saya membagi ATAU menjadi UNION, itu menghasilkan rencana standar berjalan dalam 12 detik dengan 26612 baris yang sama dikembalikan.
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Saya menafsirkan rencana ini sebagai melakukan ini:
- Dapatkan semua 41782500 baris dari Posting (jumlah aktual baris cocok dengan pemindaian CI pada Posting)
- Untuk setiap 41782500 baris dalam Posting:
- Menghasilkan skalar:
- Expr1005: OwnerUserId
- Expr1006: OwnerUserId
- Expr1004: Nilai statis 62
- Expr1008: LastEditorUserId
- Expr1009: LastEditorUserId
- Expr1007: Nilai statis 62
- Dalam gabungan:
- Exp1010: Jika Expr1005 (OwnerUserId) bukan nol, gunakan yang lain gunakan Expr1008 (LastEditorUserID)
- Expr1011: Jika Expr1006 (OwnerUserId) bukan nol, gunakan itu, kalau tidak gunakan Expr1009 (LastEditorUserId)
- Expr1012: Jika Expr1004 (62) adalah null gunakan itu, kalau tidak gunakan Expr1007 (62)
- Dalam skalar Hitung: Saya tidak tahu apa yang dilakukan ampersand.
- Expr1013: 4 [dan?] 62 (Expr1012) = 4 dan OwnerUserId NULL (NULL = Expr1010)
- Expr1014: 4 [dan?] 62 (Expr1012)
- Expr1015: 16 dan 62 (Expr1012)
- Dalam Urutan Dengan mengurutkan berdasarkan:
- Expr1013 Desc
- Expr1014 Asc
- Expr1010 Asc
- Expr1015 Desc
- Dalam Gabung Interval dihapus Expr1013 dan Expr1015 (ini adalah input tetapi bukan output)
- Dalam pencarian Indeks di bawah loop bersarang bergabung itu menggunakan Expr1010 dan Expr1011 sebagai mencari predikat, tapi saya tidak mengerti bagaimana ia memiliki akses ke ini ketika belum melakukan loop bersarang bergabung dari IX_NC_REPUTATION ke subtree yang berisi Expr1010 dan Expr1011 .
- Gabung Nested Loops hanya mengembalikan Users.IDs yang memiliki kecocokan di subtree sebelumnya. Karena pushdown predikat, semua baris yang dikembalikan dari pencarian indeks di IX_NC_REPUTATION dikembalikan.
- Gabung Nested terakhir bergabung: Untuk setiap catatan Tulisan, menghasilkan Pengguna. Di mana kecocokan ditemukan dalam dataset di bawah ini.
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND ( EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.OwnerUserId) OR EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.LastEditorUserId) ) ;
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id IN (Posts.OwnerUserId, Posts.LastEditorUserId) ) ;
Jawaban:
Rencananya mirip dengan yang saya masuki secara lebih rinci di sini .
The
Posts
meja dipindai.Untuk setiap baris ia mengekstrak
OwnerUserId
danLastEditorUserId
. Ini mirip dengan caraUNPIVOT
kerjanya. Anda melihat operator pemindaian konstan tunggal dalam rencana untuk di bawah ini menciptakan dua baris output untuk setiap baris input.Dalam hal ini rencananya sedikit lebih kompleks karena semantiknya
or
adalah bahwa jika kedua nilai kolom sama, hanya satu baris yang harus dipancarkan dari gabungan padaUsers
(bukan dua)Ini kemudian dimasukkan melalui interval penggabungan sehingga dalam hal nilai-nilai yang sama rentang runtuh ke bawah dan hanya satu pencarian dieksekusi melawan
Users
- jika tidak dua percobaan dieksekusi melawannya.Nilainya
62
adalah bendera yang berarti bahwa pencarian harus merupakan pencarian kesetaraan.Mengenai
Ini didefinisikan dalam operator gabungan berwarna kuning. Ini ada di sisi luar dari loop bersarang berwarna kuning yang disorot. Jadi ini berjalan sebelum pencarian yang disorot kuning di bagian dalam loop bersarang.
Menulis ulang yang memberikan rencana yang sama (meskipun dengan interval gabungan digantikan oleh gabungan serikat pekerja) di bawah dalam kasus ini membantu.
Bergantung pada indeks apa yang tersedia pada
Posts
tabel, varian dari kueri ini mungkin lebih efisien daripadaUNION ALL
solusi yang Anda usulkan . (salinan database yang saya tidak memiliki indeks berguna untuk ini dan solusi yang diusulkan melakukan dua pemindaian penuhPosts
. Di bawah ini melakukannya dalam satu pemindaian)sumber