Kapan predikat SARGable dapat didorong ke CTE atau tabel turunan?

15

Memaksakan

Ketika bekerja pada Top Quality Blog Posts®, saya datang di beberapa perilaku optimizer saya menemukan benar-benar menyebalkan menarik. Saya tidak segera memiliki penjelasan, setidaknya bukan yang saya senangi, jadi saya taruh di sini kalau-kalau ada orang pintar yang muncul.

Jika Anda ingin mengikuti, Anda dapat mengambil versi 2013 dari tumpukan data Stack Overflow di sini . Saya menggunakan tabel Komentar, dengan satu indeks tambahan di atasnya.

CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );

Kueri Satu

Ketika saya meminta tabel seperti itu, saya mendapatkan paket permintaan aneh .

WITH x
    AS
     (
         SELECT   TOP 101
                  c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
         ORDER BY c.Score DESC
     )
SELECT *
FROM   x
WHERE  x.Score >= 500;

GILA

Predikat SARGable tentang Skor tidak didorong di dalam CTE. Ada dalam operator filter jauh di kemudian hari dalam rencana.

GILA

Yang saya temukan aneh, karena ORDER BYberada di kolom yang sama dengan filter.

Pertanyaan Dua

Jika saya mengubah kueri, itu bisa didorong.

WITH x
    AS
     (
         SELECT   c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
     )
SELECT TOP 101 *
FROM   x
WHERE  x.Score >= 500
ORDER BY x.Score DESC;

The rencana permintaan perubahan , juga, dan berjalan lebih cepat, tanpa tumpahan ke disk. Keduanya menghasilkan hasil yang sama, dengan predikat pada pemindaian indeks nonclustered.

GILA

GILA

Kueri Tiga

Ini sama dengan menulis kueri seperti ini:

SELECT   TOP 101
         c.UserId, c.Text, c.Score
FROM     dbo.Comments AS c
WHERE c.Score >= 500
ORDER BY c.Score DESC;

Kueri Empat

Menggunakan tabel turunan mendapatkan paket kueri "buruk" yang sama dengan kueri CTE awal

SELECT *
FROM   (   SELECT   TOP 101
                    c.UserId, c.Text, c.Score
           FROM     dbo.Comments AS c
           ORDER BY c.Score DESC ) AS x
WHERE x.Score >= 500;

Segalanya menjadi lebih aneh ketika ...

Saya mengubah kueri untuk memesan data yang naik, dan filter ke <=.

Agar tidak membuat pertanyaan ini terlalu lama, saya akan menggabungkan semuanya.

Pertanyaan

--Derived table
SELECT *
FROM   (   SELECT   TOP 101
                    c.UserId, c.Text, c.Score
           FROM     dbo.Comments AS c
           ORDER BY c.Score ASC ) AS x
WHERE x.Score <= 500;


--TOP inside CTE
WITH x
    AS
     (
         SELECT   TOP 101
                  c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
         ORDER BY c.Score ASC
     )
SELECT *
FROM   x
WHERE  x.Score <= 500;


--Written normally
SELECT   TOP 101
         c.UserId, c.Text, c.Score
FROM     dbo.Comments AS c
WHERE c.Score <= 500
ORDER BY c.Score ASC;

--TOP outside CTE
WITH x
    AS
     (
         SELECT   c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
     )
SELECT TOP 101 *
FROM   x
WHERE  x.Score <= 500
ORDER BY x.Score ASC;

Rencana

Rencanakan tautan .

GILA

Perhatikan bahwa tidak satu pun dari kueri ini yang memanfaatkan indeks yang tidak dikelompokkan - satu-satunya hal yang berubah di sini adalah posisi operator filter. Dalam keadaan apa pun, predikat tidak didorong ke akses indeks.

Sebuah Pertanyaan Muncul!

Apakah ada alasan bahwa predikat SARGable dapat ditekan dalam beberapa skenario dan tidak pada skenario lainnya? Perbedaan dalam kueri yang diurutkan dalam urutan menurun itu menarik, tetapi perbedaan antara kueri dan yang naik aneh.

Bagi siapa pun yang tertarik, berikut adalah paket dengan hanya indeks di Score:

Erik Darling
sumber

Jawaban:

11

Ada beberapa masalah dalam permainan di sini.

Mendorong predikat masa lalu TOP

Pengoptimal saat ini tidak dapat mendorong predikat masa lalu TOP, bahkan dalam kasus terbatas di mana ia akan aman untuk melakukannya *. Batasan ini menjelaskan perilaku semua pertanyaan dalam pertanyaan di mana predikat berada dalam cakupan yang lebih tinggi daripada TOP.

Cara mengatasinya adalah melakukan penulisan ulang secara manual. Masalah mendasar mirip dengan kasus mendorong predikat melewati fungsi jendela , kecuali tidak ada aturan khusus yang sesuai seperti SelOnSeqPrj.

Pendapat pribadi saya adalah bahwa aturan eksplorasi seperti SelOnToptetap tidak diterapkan karena orang-orang dengan sengaja menulis pertanyaan TOPdalam upaya untuk memberikan semacam 'pagar optimasi'.

* Secara umum ini berarti predikat akan muncul di ORDER BYklausa yang terkait dengan TOP, dan arah ketidaksetaraan harus sesuai dengan arah penyortiran. Transformasi juga perlu memperhitungkan perilaku penyortiran NULLs di SQL Server. Secara keseluruhan, keterbatasan mungkin berarti transformasi ini umumnya tidak cukup berguna dalam praktik untuk membenarkan upaya eksplorasi tambahan.

Masalah biaya

Rencana pelaksanaan yang tersisa dalam pertanyaan dapat dijelaskan sebagai pilihan berbasis biaya karena distribusi nilai dalam Scorekolom (lebih banyak baris <= 500 daripada> = 500), dan pengaruh sasaran baris yang diperkenalkan oleh TOP.

Misalnya, kueri:

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score <= 500
ORDER BY
    c.Score ASC;

... menghasilkan rencana dengan predikat yang tampaknya tidak disembunyikan di dalam Filter:

filter terlambat karena tujuan baris

Perhatikan bahwa Sortir diperkirakan menghasilkan 101 baris. Ini adalah efek dari tujuan baris yang ditambahkan oleh Top. Ini memengaruhi perkiraan biaya Penyortiran dan Penyaringan yang cukup untuk membuatnya tampak seperti ini adalah pilihan yang lebih murah. Perkiraan biaya rencana ini adalah 2401,39 unit.

Jika kami menonaktifkan sasaran baris dengan petunjuk kueri:

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score <= 500
ORDER BY
    c.Score ASC
OPTION (USE HINT ('DISABLE_OPTIMIZER_ROWGOAL'));

... rencana eksekusi yang dihasilkan adalah:

rencanakan tanpa tujuan baris

Predikat tersebut telah dimasukkan ke dalam pemindaian sebagai predikat tidak-tersisa residual, dan biaya keseluruhan rencana adalah 2402,32 unit.

Perhatikan bahwa <= 500predikat tidak diharapkan untuk menyaring setiap baris. Jika Anda memilih angka yang lebih kecil, seperti <= 50, pengoptimal akan lebih suka rencana push-predicate terlepas dari efek sasaran baris.

Untuk kueri dengan Score DESCdan Score >= 500predikat:

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score >= 500
ORDER BY
    c.Score DESC;

Sekarang predikat diharapkan menjadi sangat selektif, sehingga pengoptimal memilih untuk mendorong predikat dan menggunakan indeks nonclustered dengan pencarian:

predikat selektif

Sekali lagi, pengoptimal mempertimbangkan beberapa alternatif dan memilih ini sebagai opsi yang tampaknya termurah, seperti biasa.

Paul White Reinstate Monica
sumber