Mengapa kueri serupa ini menggunakan fase optimasi yang berbeda (pemrosesan transaksi vs rencana cepat)?

12

Contoh kode dalam item sambung ini

Menunjukkan bug di mana

SELECT COUNT(*)
FROM   dbo.my_splitter_1('2') L1
       INNER JOIN dbo.my_splitter_1('') L2
         ON L1.csv_item = L2.csv_item

Mengembalikan hasil yang benar. Tetapi yang berikut ini mengembalikan hasil yang salah (pada 2014 menggunakan Cardinality Estimator yang baru)

SELECT
    (SELECT COUNT(*)
    FROM dbo.my_splitter_1('2') L1
     INNER JOIN dbo.my_splitter_1('') L2
        ON L1.csv_item = L2.csv_item)

Karena itu salah memuat hasil untuk L2 ke dalam gulungan sub ekspresi umum kemudian memutar ulang hasilnya untuk hasil L1.

Saya ingin tahu mengapa perbedaan perilaku antara dua pertanyaan. Bendera Trace 8675 menunjukkan bahwa yang berfungsi masuk search(0) - transaction processingdan yang gagal masuk search(1) - quick plan.

Jadi saya berasumsi bahwa ketersediaan aturan transformasi tambahan berada di belakang perbedaan perilaku (menonaktifkan BuildGbApply atau GenGbApplySimple tampaknya memperbaikinya misalnya).

Tetapi mengapa kedua rencana untuk kueri yang sangat mirip ini mengalami fase optimasi yang berbeda? Dari apa yang saya baca search (0)membutuhkan setidaknya tiga tabel dan kondisi itu tentu tidak terpenuhi pada contoh pertama.

Martin Smith
sumber

Jawaban:

7

Setiap tahap memiliki kondisi masuk. "Memiliki setidaknya tiga referensi tabel" adalah salah satu syarat entri yang kita bicarakan ketika memberikan contoh sederhana, tetapi itu bukan satu-satunya.

Umumnya, hanya gabungan dan serikat dasar yang diizinkan untuk masuk ke pencarian 0; subqueries skalar, semi gabung, dll. mencegah entri untuk mencari 0. Tahap ini sebenarnya untuk bentuk kueri tipe OLTP yang sangat umum. Aturan yang diperlukan untuk menjelajahi hal-hal yang kurang umum tidak diaktifkan. Kueri contoh Anda memiliki subquery skalar, sehingga gagal entri.

Itu juga tergantung bagaimana Anda menghitung referensi tabel. Saya tidak pernah melihat secara mendalam ke dalam ini dengan fungsi, tetapi mungkin logika menghitung Fungsi Table Valued serta variabel tabel yang mereka hasilkan. Bahkan bisa menghitung referensi tabel di dalam fungsi itu sendiri - saya tidak yakin; meskipun saya tahu bahwa fungsi hanya kerja keras semua.

Bug dengan GenGbApplySimplejelek. Bentuk rencana ini selalu merupakan kemungkinan, tetapi ditolak karena alasan biaya hingga perubahan ke 100-rows mengasumsikan kardinalitas variabel tabel masuk. Adalah mungkin untuk memaksa bentuk rencana yang bermasalah pada pra-2014 CE dengan sebuah USE PLANpetunjuk, misalnya.

Anda benar tentang item Hubungkan yang baru dengan masalah yang sama yang dilaporkan sebelumnya .

Untuk memberikan contoh, permintaan berikut memenuhi syarat untuk pencarian 0:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY U.c1) 
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;

Membuat perubahan kecil untuk menyertakan subquery skalar berarti langsung mencari 1:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -- Changed!
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;
Paul White 9
sumber