Kueri T-SQL menggunakan paket yang sama sekali berbeda tergantung pada jumlah baris yang saya perbarui

20

Saya memiliki pernyataan SQL UPDATE dengan klausa "TOP (X)", dan baris yang saya perbarui nilainya memiliki sekitar 4 miliar baris. Ketika saya menggunakan "TOP (10)", saya mendapatkan satu paket eksekusi yang dieksekusi hampir secara instan, tetapi ketika saya menggunakan "TOP (50)" atau lebih besar, kueri tidak pernah (setidaknya, tidak saat saya menunggu) selesai, dan menggunakan rencana eksekusi yang sama sekali berbeda. Kueri yang lebih kecil menggunakan paket yang sangat sederhana dengan sepasang indeks pencarian dan loop bersarang bergabung, di mana kueri yang sama persis (dengan jumlah baris yang berbeda dalam klausa TOP dari pernyataan UPDATE) menggunakan rencana yang melibatkan dua pencarian indeks yang berbeda , gulungan meja, paralelisme, dan banyak kerumitan lainnya.

Saya telah menggunakan "OPTION (USE PLAN ...)" untuk memaksanya menggunakan rencana eksekusi yang dihasilkan oleh kueri yang lebih kecil - ketika saya melakukan ini, saya dapat memperbarui sebanyak 100.000 baris dalam beberapa detik. Saya tahu rencana kueri itu baik, tetapi SQL Server hanya akan memilih paket itu sendiri ketika hanya sejumlah kecil baris yang terlibat - setiap jumlah baris yang lumayan besar dalam pembaruan saya akan menghasilkan rencana sub-optimal.

Saya pikir mungkin paralelisme yang harus disalahkan, jadi saya menetapkan MAXDOP 1permintaan, tetapi tidak berpengaruh - langkah itu hilang, tetapi pilihan / kinerja yang buruk tidak. Saya juga berlari sp_updatestatspagi ini untuk memastikan itu bukan penyebabnya.

Saya telah melampirkan dua paket eksekusi - yang lebih pendek juga lebih cepat. Selain itu, inilah pertanyaan dalam pertanyaan (perlu dicatat bahwa SELECT yang saya sertakan tampaknya cepat dalam kasus jumlah baris kecil dan besar):

    update top (10000) FactSubscriberUsage3
               set AccountID = sma.CustomerID
    --select top 50 f.AccountID, sma.CustomerID
      from FactSubscriberUsage3 f
      join dimTime t
        on f.TimeID = t.TimeID
      join #mac sma
        on f.macid = sma.macid
       and t.TimeValue between sma.StartDate and sma.enddate 
     where f.AccountID = 0 --There's a filtered index on the table for this

Inilah rencana singkatnya : Rencana Eksekusi Cepat

Dan inilah yang lebih lambat : Rencana Eksekusi Lambat

Apakah ada sesuatu yang jelas baik dalam cara saya mengatur permintaan saya atau dalam rencana pelaksanaan asalkan akan meminjamkan diri ke pilihan yang buruk mesin query membuat? Jika perlu, saya juga bisa memasukkan definisi tabel yang terlibat dan indeks yang ditentukan pada mereka.

Bagi mereka yang meminta versi hanya objek statistik dari database: Saya bahkan tidak menyadari Anda bisa melakukan itu, tetapi itu masuk akal! Saya mencoba membuat skrip untuk database hanya statistik sehingga orang lain dapat menguji rencana eksekusi untuk diri mereka sendiri, tetapi saya dapat menghasilkan statistik / histogram pada indeks saya yang difilter (tampaknya kesalahan sintaksis dalam skrip), jadi saya kurang beruntung di sana. Saya mencoba menghapus filter dan rencana kueri sudah dekat, tetapi tidak persis sama, dan saya tidak ingin mengirim siapa pun yang mengejar angsa.

Perbarui dan beberapa rencana eksekusi yang lebih lengkap: Pertama, Rencana Penjelajah SQL Sentry adalah alat yang luar biasa. Saya bahkan tidak tahu itu ada sampai melihat pertanyaan rencana kueri lainnya di situs ini, dan ada sedikit yang bisa dikatakan tentang bagaimana permintaan saya dieksekusi. Meskipun saya tidak yakin bagaimana cara mengatasi masalah tersebut, mereka memperjelas apa masalahnya.

Inilah ringkasan untuk 10, 100, dan 1000 baris - Anda dapat melihat bahwa kueri 1000 baris adalah jalan, jauh dari yang lain: Ringkasan Pernyataan

Anda dapat melihat bahwa kueri ketiga memiliki jumlah pembacaan yang konyol, sehingga jelas melakukan sesuatu yang sangat berbeda. Berikut perkiraan rencana eksekusi, dengan jumlah baris. Perkiraan rencana eksekusi 1000-baris: Perkiraan rencana eksekusi 1000-baris

Dan inilah hasil aktual dari rencana eksekusi (omong-omong, dengan "tidak pernah selesai", ternyata yang saya maksudkan adalah "selesai dalam satu jam"). Rencana pelaksanaan aktual 1000-baris Rencana pelaksanaan aktual 1000-baris

Hal pertama yang saya perhatikan adalah bahwa, alih-alih menarik 60k baris dari tabel dimTime seperti itu mengharapkan, itu sebenarnya menarik 1,6 miliar, dengan B . Melihat permintaan saya, saya tidak yakin bagaimana ini menarik kembali banyak baris dari tabel dimTime. Operator ANTARA yang saya gunakan hanya memastikan bahwa saya menarik catatan yang benar dari #mac berdasarkan catatan waktu pada tabel Fakta. Namun, ketika saya menambahkan baris ke klausa WHERE tempat saya memfilter t.TimeValue (atau t.TimeID) ke nilai tunggal, saya berhasil memperbarui 100.000 baris dalam hitungan detik. Sebagai akibatnya, dan sebagaimana dijelaskan dalam rencana eksekusi yang saya sertakan, sudah jelas bahwa tabel waktu saya adalah masalahnya, tetapi saya tidak yakin bagaimana saya akan mengubah kriteria bergabung untuk mengatasi masalah ini dan menjaga akurasi. . Adakah pikiran?

Untuk referensi, berikut paket (dengan jumlah baris) untuk pembaruan 100 baris. Anda dapat melihat bahwa itu menyentuh indeks yang sama, dan masih dengan satu ton baris, tetapi tidak jauh dari masalah yang sama. Eksekusi 100 baris dengan jumlah baris : masukkan deskripsi gambar di sini

SqlRyan
sumber
Ini adalah statistik GOTTA Be. Apakah Anda menjalankan sp_updatestatisticsdi atas meja?
JNK
@ JNK: Awalnya saya pikir begitu, tetapi sudah menjalankan sp_updatestats tanpa perubahan. Saya hanya menjalankannya lagi dan tidak peduli untuk memperbarui statistik pada indeks yang terlibat dalam kueri. Terimakasih Meskipun!
SqlRyan
Yang kedua adalah rencana pembaruan yang lebar (per indeks) daripada yang sempit (per baris) yang menjelaskan beberapa kompleksitas tambahan yang terlihat. Tapi sungguh satu-satunya perbedaan adalah bergabung dengan pesanan from #mac sma join f on f.macid = sma.macid join dimTime t on f.TimeID = t.TimeID and t.TimeValue between sma.StartDate and sma.enddatevsfrom #mac join t on t.TimeValue between sma.StartDate and sma.enddate join f on f.TimeID = t.TimeID and f.macid = sma.macid
Martin Smith
Ada yang tidak beres di sini. Bahkan rencana permintaan yang mahal harus menghasilkan baris secara bertahap. A TOP 50harus tetap mengeksekusi dengan cepat. Bisakah Anda mengunggah paket XML? Saya perlu melihat jumlah baris. Bisakah Anda menjalankan TOP 50dengan maxdop 1 dan sebagai pilih, bukan sebagai pembaruan dan memposting rencana? (Mencoba menyederhanakan / membagi dua ruang pencarian).
usr
@ usr bergabung di t.TimeValue between sma.StartDate and sma.enddatemungkin berakhir menghasilkan lebih banyak baris yang tidak berguna yang kemudian disaring dalam bergabung dengan FactSubscriber dan jadi tidak berakhir pada hasil akhir.
Martin Smith

Jawaban:

3

Indeks pada dimTime berubah. Paket yang lebih cepat menggunakan indeks _dta. Pertama, pastikan itu tidak ditandai sebagai indeks hipotetis di sys.indexes.

Mengira Anda bisa melewati beberapa parameterisasi dengan menggunakan tabel #mac untuk memfilter alih-alih hanya memasok tanggal mulai / berakhir seperti ini. WHERE t.TimeValue antara @StartDate dan @enddate. Singkirkan tabel temp itu.

william_a_dba
sumber
Indeks awalan dta sepertinya dibuat dengan mengikuti rekomendasi DTA tanpa mengubahsuaikan namanya. Indeks hipotetis tidak dapat muncul dalam rencana eksekusi aktual (dan tidak akan diestimasi baik tanpa beberapa perintah tidak berdokumen). Tidak yakin bagaimana saran kedua Anda akan bekerja. t.TimeValue between sma.StartDate and sma.enddatedikorelasikan sehingga dapat berubah untuk setiap baris dalam #temptabel. Dengan apa OP akan menggantikannya?
Martin Smith
Cukup adil, saya tidak cukup memperhatikan tabel temp.
william_a_dba
1
Namun, indeks hipotetis memang dapat mengacaukan rencana eksekusi. Jika ini hipotetis, itu harus dijatuhkan dan diciptakan kembali. blogs.technet.com/b/anurag_sharma/archive/2008/04/15/…
william_a_dba
Indeks hipotetis ditinggalkan ketika DTA tidak selesai / macet sebelum selesai. Anda harus membersihkannya secara manual jika ada gangguan dengan DTA.
william_a_dba
1
@william_a_dba - Ah, saya mengerti maksud Anda sekarang (setelah membaca tautan Anda). Kueri yang tidak pernah selesai bisa jadi terus dikompilasi ulang. Menarik!
Martin Smith
1

Tanpa informasi lebih lanjut tentang jumlah baris dalam rencana, rekomendasi awal saya adalah mengatur urutan bergabung yang benar dalam kueri dan memaksanya menggunakannya OPTION (FORCE ORDER). Menegakkan urutan bergabung dari rencana pertama.

usr
sumber