Mengapa ORDER BY saya mengurutkan dua tabel sebelum KECUALI (lambat) dan bukan setelah (cepat)?

12

SQL server 2008 R2 query optimizer puzzle

Kami memiliki dua tabel, keduanya berisi 9 juta baris. 70.000 baris berbeda, yang lain sama.

Ini cepat, 13 detik,

select * from bigtable1
except select * from similar_bigtable2

Ini memilah output dan juga cepat, 13 detik juga,

select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column

Meskipun ini sangat lambat:

;with q as (
    select * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

Dan bahkan "trik" yang kadang-kadang saya gunakan untuk mengisyaratkan SQL Server bahwa ia perlu menghitung ulang bagian tertentu dari kueri sebelum melanjutkan, tidak berfungsi dan menghasilkan kueri lambat juga:

;with q as (
    select top 100 percent * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

Melihat rencana kueri alasannya tidak sulit ditemukan:

Rencana kueri Rencana kueri dengan ORDER OLEH

SQL Server menempatkan dua jenis 9 juta baris sebelum hashmatch, sementara saya lebih suka untuk menambahkan hanya satu jenis 70.000 baris setelah hashmatch.

Jadi pertanyaannya: bagaimana saya bisa memerintahkan pengoptimal kueri untuk melakukan itu?

thomaspaulb
sumber
3
Itu tidak mengurutkan sebelum hashmatch, itu mengurutkan dan kemudian melakukan gabungan-gabung (bukan hash-gabung). Mungkin ada petunjuk untuk memaksa hash-join (atau mencegah gabungan-gabung)?
Thilo
3
Tampaknya pengoptimal kueri SQL Server menentukan bahwa menyortir data bermanfaat sehingga dapat menggunakan Gabung Gabung yang jauh lebih cepat (yang hanya berfungsi untuk data yang diurutkan) daripada Hash Match Join atau Nested Loop Join ....
marc_s
9
Sudahkah Anda mencoba alternatif EXCEPT(misalnya OUTER JOIN)? Saya menyadari sintaksnya kurang nyaman tetapi Anda mungkin dapat bermain dengan petunjuk indeks / bergabung lebih baik di sana (atau Anda mungkin tidak perlu). Alternatif yang Anda gunakan sekarang (memasukkan item ke tabel #temp terlebih dahulu) adalah solusi terakhir tetapi dalam beberapa kasus adalah satu-satunya cara untuk memaksa pengoptimal untuk sepenuhnya memisahkan dua bagian dari kueri dengan cara yang Anda inginkan.
Aaron Bertrand

Jawaban:

1

Perbedaan utama antara kedua paket kueri ini sebenarnya dalam perbedaan Hash Match dan Gabung Bergabung. Pencocokan Hash lebih efisien dan karena Anda dapat melihat kueri berjalan lebih cepat di opsi 1 (tidak menggunakan CTE).

CTE adalah alat yang hebat, tetapi tampaknya tidak efisien dalam dua kasus, Predikat Kompleks atau Kunci Induk / Anak yang Tidak Unik. Dalam kasus Anda tidak ada kunci unik dan SQL server harus mengurutkan set data terlebih dahulu untuk dapat memenuhi kebutuhan Anda. Lihat tautan di bawah ini yang memberi tahu Anda lebih banyak tentang masalah ini: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx

Jadi sepertinya Anda harus menerima kelambatannya atau menulis ulang logikanya dengan WHILE loop yang bisa lebih efisien.

Langit
sumber
0

Coba ini, lebih baik?

select * from
(
    select * from bigtable1
    except 
    select * from similar_bigtable2
) t
order by sort_column
Gordon Bell
sumber
0

Ini bukan solusi yang ideal tetapi jika Anda tidak dapat menyusun tsql untuk menghasilkan rencana yang efisien, Anda dapat menetapkan panduan rencana untuk memaksa rencana yang Anda inginkan. Melakukan ini akan berarti bahwa jika rencana yang lebih efisien tersedia, SQL tidak akan mempertimbangkannya tetapi itu adalah pilihan.

cfradenburg
sumber