Mengapa operator Gabungan memperkirakan baris lebih sedikit dari inputnya?


20

Dalam cuplikan rencana kueri berikut ini, tampak jelas bahwa estimasi baris untuk Concatenationoperator seharusnya ~4.3 billion rows, atau jumlah estimasi baris untuk dua inputnya.

Namun, perkiraan ~238 million rowsdihasilkan, mengarah ke sub-optimal Sort/ Stream Aggregatestrategi yang menumpahkan ratusan GB data ke tempdb. Perkiraan yang konsisten secara logis dalam kasus ini akan menghasilkan Hash Aggregate, menghilangkan tumpahan, dan meningkatkan kinerja kueri.

Apakah ini bug di SQL Server 2014? Apakah ada keadaan yang valid di mana perkiraan lebih rendah dari input bisa masuk akal? Solusi apa yang mungkin tersedia?

masukkan deskripsi gambar di sini

Ini adalah paket kueri lengkap (dianonimkan). Saya tidak memiliki akses sysadmin ke server ini untuk memberikan output dari QUERYTRACEON 2363atau tanda jejak serupa, tetapi mungkin bisa mendapatkan output ini dari admin jika mereka akan membantu.

Basis data berada di tingkat kompatibilitas 120, dan oleh karena itu menggunakan SQL Server 2014 Cardinality Estimator baru.

Stat diperbarui secara manual setiap kali data dimuat. Mengingat volume data, kami saat ini menggunakan laju pengambilan sampel default. Mungkin saja laju sampling yang lebih tinggi (atau FULLSCAN) dapat berdampak.

Jawaban:


21

Mengutip Campbell Fraser pada item Connect ini :

"Ketidakkonsistenan kardinalitas" ini dapat muncul dalam sejumlah situasi, termasuk ketika concat digunakan. Mereka dapat muncul karena estimasi subtree tertentu dalam rencana akhir mungkin telah dibuat pada subtree yang terstruktur berbeda tetapi secara logis setara. Karena sifat statistik estimasi kardinalitas, memperkirakan pohon yang berbeda tetapi secara logis setara tidak dijamin untuk mendapatkan perkiraan yang sama. Jadi secara keseluruhan tidak ada jaminan konsistensi yang diharapkan disediakan.

Untuk sedikit memperluas: Cara saya ingin menjelaskannya adalah dengan mengatakan bahwa perkiraan kardinalitas awal (dilakukan sebelum optimasi berbasis biaya dimulai) menghasilkan perkiraan kardinalitas yang lebih "konsisten", karena seluruh pohon awal diproses, dengan masing-masing berikutnya Estimasi tergantung langsung pada yang sebelumnya.

Selama optimasi berbasis biaya, bagian dari pohon paket (satu atau lebih operator) dapat dieksplorasi dan diganti dengan alternatif, yang masing-masing mungkin memerlukan perkiraan kardinalitas baru. Tidak ada cara umum untuk mengatakan estimasi mana yang umumnya lebih baik daripada yang lain, sehingga sangat mungkin untuk berakhir dengan rencana akhir yang tampak "tidak konsisten". Ini hanyalah hasil dari penjahitan bersama "potongan rencana" untuk membentuk pengaturan akhir.

Semua yang mengatakan, ada beberapa perubahan terperinci untuk penaksir kardinalitas baru (CE) yang diperkenalkan di SQL Server 2014 yang membuat ini agak kurang umum daripada kasus dengan CE asli.

Selain memutakhirkan ke Pembaruan Kumulatif terbaru dan memeriksa bahwa perbaikan optimizer dengan 4199 aktif, opsi utama Anda adalah untuk mencoba perubahan statistik / indeks (mencatat peringatan untuk indeks yang hilang) dan memperbarui, atau mengungkapkan kueri secara berbeda. Tujuannya adalah untuk mendapatkan rencana yang menampilkan perilaku yang Anda butuhkan. Ini kemudian dapat dibekukan dengan panduan rencana, misalnya.

Paket anonim membuat sulit untuk menilai detail, tetapi saya juga akan melihat dengan hati-hati pada bitmap untuk melihat apakah mereka dari 'dioptimalkan' (Opt_Bitmap) atau variasi pasca-optimasi (Bitmap). Saya juga curiga dengan Filter.

Namun, jika jumlah baris seperti akurat, sepertinya ini adalah kueri yang mungkin mendapat manfaat dari kolomstore. Terlepas dari manfaat yang biasa, Anda mungkin dapat memanfaatkan hibah memori dinamis untuk operator mode batch ( bendera jejak 9389 mungkin diperlukan).


7

Membangun test bed yang diakui agak sederhana pada SQL Server 2012 (11.0.6020) memungkinkan saya untuk membuat ulang rencana dengan dua query hash yang cocok digabungkan melalui a UNION ALL. Tempat tidur pengujian saya tidak menampilkan perkiraan yang salah yang Anda lihat. Mungkin ini adalah masalah SQL Server 2014 CE.

Saya mendapatkan perkiraan 133.785 baris untuk kueri yang benar-benar mengembalikan 280 baris, namun itu diharapkan karena kita akan melihat lebih jauh ke bawah:

IF OBJECT_ID('dbo.Union1') IS NOT NULL
DROP TABLE dbo.Union1;
CREATE TABLE dbo.Union1
(
    Union1_ID INT NOT NULL
        CONSTRAINT PK_Union1
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Union1_Text VARCHAR(255) NOT NULL
    , Union1_ObjectID INT NOT NULL
);

IF OBJECT_ID('dbo.Union2') IS NOT NULL
DROP TABLE dbo.Union2;
CREATE TABLE dbo.Union2
(
    Union2_ID INT NOT NULL
        CONSTRAINT PK_Union2
        PRIMARY KEY CLUSTERED
        IDENTITY(2,2)
    , Union2_Text VARCHAR(255) NOT NULL
    , Union2_ObjectID INT NOT NULL
);

INSERT INTO dbo.Union1 (Union1_Text, Union1_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;

INSERT INTO dbo.Union2 (Union2_Text, Union2_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;
GO

SELECT *
FROM dbo.Union1 u1
    INNER HASH JOIN sys.objects o ON u1.Union1_ObjectID = o.object_id
UNION ALL
SELECT *
FROM dbo.Union2 u2
    INNER HASH JOIN sys.objects o ON u2.Union2_ObjectID = o.object_id;

Saya pikir alasannya adalah sekitar kurangnya statistik untuk dua gabungan yang dihasilkan yang UNIONed. SQL Server perlu membuat tebakan dalam kebanyakan kasus seputar selektivitas kolom ketika dihadapkan dengan kurangnya statistik.

Joe Sack memiliki bacaan yang menarik tentang hal itu di sini .

Untuk a UNION ALL, aman untuk mengatakan kita akan melihat jumlah total baris yang dikembalikan oleh masing-masing komponen serikat, namun karena SQL Server menggunakan estimasi baris untuk dua komponen UNION ALL, kita melihatnya menambahkan total estimasi baris dari kedua pertanyaan untuk datang dengan perkiraan untuk operator gabungan.

Dalam contoh saya di atas, perkiraan jumlah baris untuk masing-masing bagian UNION ALLadalah 66,8927, yang ketika dijumlahkan sama dengan 133,785, yang kita lihat untuk perkiraan jumlah baris untuk operator gabungan.

Rencana eksekusi aktual untuk kueri gabungan di atas terlihat seperti:

masukkan deskripsi gambar di sini

Anda dapat melihat jumlah baris "diperkirakan" vs "aktual". Dalam kasus saya, menambahkan "perkiraan" jumlah baris yang dikembalikan oleh dua operator hash dengan tepat sama dengan jumlah yang ditunjukkan oleh operator gabungan.

Saya akan mencoba untuk mendapatkan hasil dari jejak 2363 dll seperti yang direkomendasikan dalam posting Paul White yang Anda tunjukkan dalam pertanyaan Anda. Sebagai alternatif, Anda dapat mencoba menggunakan OPTION (QUERYTRACEON 9481)di kueri untuk kembali ke versi 70 CE untuk melihat apakah itu "memperbaiki" masalah.


1
Terima kasih. Saya benar-benar melihat "alasannya adalah kurangnya statistik untuk dua gabungan yang dihasilkan yang UNIONed" memiliki dampak besar pada bergabung atau agregasi berikutnya (yang terjadi setelah UNION). SQL 2014 sebenarnya menangani ini lebih baik daripada SQL 2012 dalam pengalaman saya. Berikut ini skrip tes sederhana yang pernah saya gunakan di masa lalu misalnya: gist.github.com/anonymous/1497112d8b25ab8fb782a04569959c68 Namun, saya tidak akan berpikir bahwa operator Concatenation akan memerlukan jenis informasi yang sama tentang distribusi nilai yang bergabung dengan mungkin membutuhkan.
Geoff Patterson

Saya setuju dengan Anda bahwa rangkaian seharusnya tidak memerlukan statistik untuk melakukan secara akurat. Itu hanya harus dapat dengan andal menambahkan perkiraan baris yang masuk untuk mendapatkan pengertian yang baik tentang jumlah baris yang akan dihasilkan. Seperti yang ditunjukkan oleh @PaulWhite dalam jawabannya yang secara mengejutkan tidak selalu demikian. Bagi saya takeaway di sini mungkin terlihat sederhana, tetapi dalam kenyataannya mungkin tidak. Saya sangat senang Anda mengajukan pertanyaan seperti yang Anda lakukan, saya hanya berharap Anda tidak perlu menganonimkan rencana tersebut - akan menarik untuk melihat permintaan yang sebenarnya.
Max Vernon
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.