Kueri berjalan cepat:
DECLARE @SessionGUID uniqueidentifier
SET @SessionGUID = 'BCBA333C-B6A1-4155-9833-C495F22EA908'
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
biaya subtree: 0,502
Tetapi menempatkan SQL yang sama dalam prosedur tersimpan berjalan lambat, dan dengan rencana eksekusi yang sama sekali berbeda
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
EXECUTE ViewOpener @SessionGUID
Biaya subtree: 19.2
Saya sudah lari
sp_recompile ViewOpener
Dan itu masih berjalan sama (buruk), dan saya juga mengubah prosedur tersimpan menjadi
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
SELECT *, 'recompile please'
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
Dan kembali lagi, mencoba untuk benar-benar mengelabui agar kompilasi ulang.
Saya telah menjatuhkan dan menciptakan kembali prosedur tersimpan untuk mendapatkannya agar menghasilkan rencana baru.
Saya sudah mencoba memaksa kompilasi ulang, dan mencegah pengendusan parameter , dengan menggunakan variabel umpan:
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
DECLARE @SessionGUIDbitch uniqueidentifier
SET @SessionGUIDbitch = @SessionGUID
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUIDbitch
ORDER BY CurrencyTypeOrder, Rank
Saya juga mencoba mendefinisikan prosedur tersimpan WITH RECOMPILE
:
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier
WITH RECOMPILE
AS
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
Sehingga rencananya tidak pernah di-cache, dan saya sudah mencoba memaksa kompilasi ulang pada eksekusi:
EXECUTE ViewOpener @SessionGUID WITH RECOMPILE
Itu tidak membantu.
Saya sudah mencoba mengubah prosedur menjadi SQL dinamis:
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier
WITH RECOMPILE AS
DECLARE @SQLString NVARCHAR(500)
SET @SQLString = N'SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank'
EXECUTE sp_executesql @SQLString,
N'@SessionGUID uniqueidentifier',
@SessionGUID
Itu tidak membantu.
Entitas "Report_Opener
" adalah tampilan, yang tidak diindeks. Tampilan hanya referensi tabel yang mendasarinya. Tidak ada tabel berisi kolom yang dihitung, diindeks atau sebaliknya.
Untuk itu saya mencoba membuat tampilan dengan
SET ANSI_NULLS ON
SET QUOTED_IDENTIFER ON
Itu tidak memperbaikinya.
Bagaimana itu?
- pertanyaannya cepat
- memindahkan kueri ke tampilan, dan memilih dari tampilan dengan cepat
- memilih dari tampilan dari prosedur tersimpan 40x lebih lambat?
Saya mencoba memindahkan definisi tampilan langsung ke prosedur tersimpan (melanggar 3 aturan bisnis, dan melanggar enkapsulasi penting), dan itu membuatnya hanya sekitar 6x lebih lambat.
Mengapa versi prosedur tersimpan sangat lambat? Apa yang bisa menjelaskan SQL Server menjalankan ad-hoc SQL lebih cepat daripada berbagai jenis ad-hoc SQL?
Saya lebih suka tidak
- embed SQL dalam kode
ubah kode sama sekali
Microsoft SQL Server 2000 - 8.00.2050 (Intel X86) Mar 7 2008 21:29:56 Copyright (c) 1988-2003 Microsoft Corporation Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
Tapi apa yang bisa menjelaskan SQL Server tidak dapat berjalan secepat SQL Sever menjalankan kueri, jika bukan parameter sniffing.
Upaya saya berikutnya adalah StoredProcedureA
meminta StoredProcedureB
panggilan StoredProcedureC
panggilan StoredProcedureD
untuk menanyakan tampilan.
Dan jika gagal, minta prosedur tersimpan memanggil prosedur tersimpan, memanggil UDF, memanggil UDF, memanggil prosedur tersimpan, memanggil UDF untuk menanyakan tampilan.
Singkatnya, berikut ini berjalan cepat dari QA, tetapi lambat ketika dimasukkan ke dalam prosedur tersimpan:
Asli:
--Runs fine outside of a stored procedure
SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
sp_executesql
:
--Runs fine outside of a stored procedure
DECLARE @SQLString NVARCHAR(500)
SET @SQLString = N'SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank'
EXECUTE sp_executesql @SQLString,
N'@SessionGUID uniqueidentifier',
@SessionGUID
EXEC(@sql)
:
--Runs fine outside of a stored procedure
DECLARE @sql NVARCHAR(500)
SET @sql = N'SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = '''+CAST(@SessionGUID AS varchar(50))+'''
ORDER BY CurrencyTypeOrder, Rank'
EXEC(@sql)
Rencana Eksekusi
The baik rencana:
|--Sort(ORDER BY:([Expr1020] ASC, [Currencies].[Rank] ASC))
|--Compute Scalar(DEFINE:([Expr1020]=If ([Currencies].[CurrencyType]='ctCanadianCash') then 1 else If ([Currencies].[CurrencyType]='ctMiscellaneous') then 2 else If ([Currencies].[CurrencyType]='ctTokens') then 3 else If ([Currencies].[CurrencyType]
|--Nested Loops(Left Outer Join, OUTER REFERENCES:([Openers].[OpenerGUID]))
|--Filter(WHERE:((([Currencies].[IsActive]<>0 AND [Currencies].[OnOpener]<>0) AND ((((((([Currencies].[CurrencyType]='ctUSCoin' OR [Currencies].[CurrencyType]='ctMiscellaneousUS') OR [Currencies].[CurrencyType]='ctUSCash') OR [Currencies].
| |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Currencies].[CurrencyGUID], [Openers].[OpenerGUID]) WITH PREFETCH)
| |--Nested Loops(Left Outer Join)
| | |--Bookmark Lookup(BOOKMARK:([Bmk1016]), OBJECT:([GrobManagementSystemLive].[dbo].[Windows]))
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([Openers].[WindowGUID]))
| | | |--Bookmark Lookup(BOOKMARK:([Bmk1014]), OBJECT:([GrobManagementSystemLive].[dbo].[Openers]))
| | | | |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_SessionGUID]), SEEK:([Openers].[SessionGUID]=[@SessionGUID]) ORDERED FORWARD)
| | | |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Windows].[IX_Windows]), SEEK:([Windows].[WindowGUID]=[Openers].[WindowGUID]) ORDERED FORWARD)
| | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType]))
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID] AND [OpenerDetails].[CurrencyGUID]=[Currenc
|--Hash Match(Cache, HASH:([Openers].[OpenerGUID]), RESIDUAL:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]))
|--Stream Aggregate(DEFINE:([Expr1006]=SUM(If (((([Currencies].[CurrencyType]='ctMiscellaneous' OR [Currencies].[CurrencyType]='ctTokens') OR [Currencies].[CurrencyType]='ctChips') OR [Currencies].[CurrencyType]='ctCanadianCoin') OR [
|--Nested Loops(Inner Join, OUTER REFERENCES:([OpenerDetails].[CurrencyGUID]) WITH PREFETCH)
|--Nested Loops(Inner Join)
| |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_OneOpenerPerSession]), SEEK:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
|--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[PK_Currencies_CurrencyGUID]), SEEK:([Currencies].[CurrencyGUID]=[OpenerDetails].[CurrencyGUID]) ORDERED FORWARD)
The buruk Rencana
|--Sort(ORDER BY:([Expr1020] ASC, [Currencies].[Rank] ASC))
|--Compute Scalar(DEFINE:([Expr1020]=If ([Currencies].[CurrencyType]='ctCanadianCash') then 1 else If ([Currencies].[CurrencyType]='ctMiscellaneous') then 2 else If ([Currencies].[CurrencyType]='ctTokens') then 3 else If ([Currencies].[Currency
|--Nested Loops(Left Outer Join, OUTER REFERENCES:([Openers].[OpenerGUID]))
|--Filter(WHERE:((([Currencies].[IsActive]<>0 AND [Currencies].[OnOpener]<>0) AND ((((((([Currencies].[CurrencyType]='ctUSCoin' OR [Currencies].[CurrencyType]='ctMiscellaneousUS') OR [Currencies].[CurrencyType]='ctUSCash') OR [Currenc
| |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Currencies].[CurrencyGUID], [Openers].[OpenerGUID]) WITH PREFETCH)
| |--Filter(WHERE:([Openers].[SessionGUID]=[@SessionGUID]))
| | |--Concatenation
| | |--Nested Loops(Left Outer Join)
| | | |--Table Spool
| | | | |--Hash Match(Inner Join, HASH:([Windows].[WindowGUID])=([Openers].[WindowGUID]), RESIDUAL:([Windows].[WindowGUID]=[Openers].[WindowGUID]))
| | | | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Windows].[IX_Windows_CageGUID]))
| | | | |--Table Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Openers]))
| | | |--Table Spool
| | | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType]))
| | |--Compute Scalar(DEFINE:([Openers].[OpenerGUID]=NULL, [Openers].[SessionGUID]=NULL, [Windows].[UseChipDenominations]=NULL))
| | |--Nested Loops(Left Anti Semi Join)
| | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType]))
| | |--Row Count Spool
| | |--Table Spool
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID] AND [OpenerDetails].[CurrencyGUID]=[Cu
|--Hash Match(Cache, HASH:([Openers].[OpenerGUID]), RESIDUAL:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]))
|--Stream Aggregate(DEFINE:([Expr1006]=SUM([partialagg1034]), [Expr1007]=SUM([partialagg1035]), [Expr1008]=SUM([partialagg1036]), [Expr1009]=SUM([partialagg1037]), [Expr1010]=SUM([partialagg1038]), [Expr1011]=SUM([partialagg1039]
|--Nested Loops(Inner Join)
|--Stream Aggregate(DEFINE:([partialagg1034]=SUM(If (((([Currencies].[CurrencyType]='ctMiscellaneous' OR [Currencies].[CurrencyType]='ctTokens') OR [Currencies].[CurrencyType]='ctChips') OR [Currencies].[CurrencyType]='
| |--Nested Loops(Inner Join, OUTER REFERENCES:([OpenerDetails].[CurrencyGUID]) WITH PREFETCH)
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
| |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[PK_Currencies_CurrencyGUID]), SEEK:([Currencies].[CurrencyGUID]=[OpenerDetails].[CurrencyGUID]) ORDERED FORWARD)
|--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_OneOpenerPerSession]), SEEK:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
Yang buruk sangat bersemangat mengumpulkan 6 juta baris; yang lain tidak.
Catatan: Ini bukan pertanyaan tentang menyetel kueri. Saya memiliki pertanyaan yang menjalankan kilat dengan cepat. Saya hanya ingin SQL Server berjalan cepat dari prosedur tersimpan.
sumber
Jawaban:
Saya memiliki masalah yang sama dengan poster aslinya tetapi jawaban yang dikutip tidak menyelesaikan masalah bagi saya. Permintaan masih berjalan sangat lambat dari prosedur tersimpan.
Saya menemukan jawaban lain di sini "Parameter Sniffing" , Terima kasih Omnibuzz. Intinya menggunakan "Variabel lokal" di kueri prosedur tersimpan Anda, tetapi bacalah yang asli untuk pemahaman lebih lanjut, ini adalah penulisan yang bagus. misalnya
Cara lambat:
Cara cepat:
Semoga ini bisa membantu orang lain, melakukan ini mengurangi waktu eksekusi saya dari 5+ menit menjadi sekitar 6-7 detik.
sumber
WITH RECOMPILE
tidak membuat perbedaan bagi saya, hanya parameter lokal.Saya menemukan masalahnya, inilah skrip versi lambat dan cepat dari prosedur tersimpan:
dbo.ViewOpener__RenamedForCruachan__Slow.PRC
dbo.ViewOpener__RenamedForCruachan__Fast.PRC
Jika Anda tidak melihat perbedaannya, saya tidak menyalahkan Anda. Perbedaannya tidak dalam prosedur yang tersimpan sama sekali. Perbedaan yang mengubah kueri 0,5 biaya cepat menjadi yang melakukan spool bersemangat 6 juta baris:
Lambat:
SET ANSI_NULLS OFF
Cepat:
SET ANSI_NULLS ON
Jawaban ini juga bisa masuk akal, karena pandangan memang memiliki klausa gabungan yang mengatakan:
Jadi ada beberapa
NULL
yang terlibat.Penjelasan lebih lanjut dibuktikan dengan kembali ke Query Analizer, dan berlari
.
.
Dan permintaannya lambat.
Jadi masalahnya bukan karena kueri dijalankan dari prosedur tersimpan. Masalahnya adalah bahwa opsi default koneksi Enterprise Manager
ANSI_NULLS off
, bukanANSI_NULLS on
, yang merupakan standar QA.Microsoft mengakui fakta ini di KB296769 (BUG: Tidak dapat menggunakan SQL Enterprise Manager untuk membuat prosedur tersimpan yang berisi objek server tertaut). Solusinya adalah menyertakan
ANSI_NULLS
opsi dalam dialog prosedur tersimpan:sumber
ANSI_NULLS ON
membuat perbedaan kinerja yang sangat besar.JOIN
klausa di dalam tampilan memiliki arti yang berbeda kapanANSI_NULLS OFF
. Tiba-tiba baris cocok, menyebabkan pengoptimal menjalankan kueri sepenuhnya berbeda. Bayangkan bahwa alih-alih menghilangkan 99,9% dari semua baris, mereka tiba-tiba kembali.ANSI_NULLS OFF
sudah usang dan dianggap sebagai praktik burukLakukan ini untuk basis data Anda. Saya memiliki masalah yang sama - ini berfungsi dengan baik dalam satu basis data tetapi ketika saya menyalin basis data ini ke yang lain menggunakan Impor SSIS (bukan pengembalian yang biasa), masalah ini terjadi pada sebagian besar prosedur tersimpan saya. Jadi setelah googling lagi, saya menemukan blog Pinal Dave (yang mana, saya menemukan sebagian besar posnya dan memang banyak membantu saya jadi terima kasih Pinal Dave) .
Saya menjalankan kueri di bawah ini pada basis data saya dan itu memperbaiki masalah saya:
Semoga ini membantu. Hanya melewati bantuan dari orang lain yang membantu saya.
sumber
DBCC REINDEX
telah usang, jadi Anda harus mencari alternatif.DBCC DBREINDEX
, MS mengatakan: "Fitur ini akan dihapus dalam versi Microsoft SQL Server di masa depan. Jangan gunakan fitur ini dalam pekerjaan pengembangan baru, dan modifikasi aplikasi yang saat ini menggunakan fitur ini sesegera mungkin. Gunakan ALTER INDEX sebagai gantinya."Saya menghadapi masalah yang sama & posting ini sangat membantu saya tetapi tidak ada jawaban yang diposting yang menyelesaikan masalah spesifik saya. Saya ingin memposting solusi yang bekerja untuk saya dengan harapan dapat membantu orang lain.
https://stackoverflow.com/a/24016676/814299
sumber
Kali ini Anda menemukan masalah Anda. Jika lain kali Anda kurang beruntung dan tidak dapat mengetahuinya, Anda dapat menggunakan pembekuan paket dan berhenti mengkhawatirkan rencana eksekusi yang salah.
sumber
Saya mengalami masalah ini. Kueri saya terlihat seperti:
Prosedur tersimpan saya didefinisikan seperti:
Saya mengubah tipe data menjadi datetime dan voila! Pergi dari 30 menit hingga 1 menit!
sumber
Sudahkah Anda mencoba membangun kembali statistik dan / atau indeks pada tabel Report_Opener. Semua kompilasi dari SP tidak akan berarti apa-apa jika statistik masih menunjukkan data dari saat database pertama kali tidak diresmikan.
Kueri awal itu sendiri bekerja dengan cepat karena pengoptimal dapat melihat bahwa parameter tidak akan pernah menjadi nol. Dalam kasus SP, pengoptimal tidak dapat memastikan bahwa parameter tidak akan pernah menjadi nol.
sumber
Meskipun saya biasanya menentangnya (meskipun dalam kasus ini sepertinya Anda memiliki alasan yang tulus), apakah Anda sudah mencoba memberikan petunjuk kueri pada versi SP dari kueri? Jika SQL Server sedang mempersiapkan rencana eksekusi yang berbeda dalam dua contoh itu, dapatkah Anda menggunakan petunjuk untuk menentukan indeks apa yang akan digunakan, sehingga paket tersebut cocok dengan yang pertama?
Untuk beberapa contoh, Anda bisa pergi ke sini .
EDIT: Jika Anda dapat memposting rencana kueri Anda di sini, mungkin kami dapat mengidentifikasi beberapa perbedaan antara rencana yang memberitahu.
KEDUA: Memperbarui tautan menjadi spesifik SQL-2000. Anda harus menggulir ke bawah cara, tetapi ada yang kedua berjudul "Petunjuk Meja" itulah yang Anda cari.
KETIGA: Kueri "Buruk" tampaknya mengabaikan [IX_Openers_SessionGUID] pada tabel "Pembuka" - ada kemungkinan menambahkan petunjuk INDEX untuk memaksanya menggunakan indeks yang akan mengubah banyak hal?
sumber
Ini mungkin tidak mungkin, tetapi mengingat bahwa perilaku Anda yang diamati tidak biasa itu perlu diperiksa dan tidak ada orang lain yang menyebutkannya.
Apakah kamu benar-benar yakin bahwa semua objek dimiliki oleh dbo dan Anda tidak memiliki salinan jahat yang dimiliki oleh Anda sendiri atau yang hadir juga oleh pengguna lain?
Hanya kadang-kadang ketika saya melihat perilaku aneh itu karena sebenarnya ada dua salinan dari suatu objek dan mana yang Anda dapatkan tergantung pada apa yang ditentukan dan siapa Anda masuk sebagai. Misalnya, sangat mungkin untuk memiliki dua salinan tampilan atau prosedur dengan nama yang sama tetapi dimiliki oleh pemilik yang berbeda - situasi yang dapat muncul ketika Anda tidak masuk ke database sebagai dbo dan lupa untuk menentukan dbo sebagai pemilik objek saat Anda membuat objek.
Perhatikan bahwa dalam teks Anda menjalankan beberapa hal tanpa menentukan pemilik, mis
jika misalnya ada di mana dua salinan viewOpener hadir dimiliki oleh dbo dan [beberapa pengguna lain] maka yang mana Anda benar-benar kompilasi ulang jika Anda tidak menentukan tergantung pada keadaan. Begitu juga dengan tampilan Report_Opener - jika ada di mana dua salinan (dan mereka dapat berbeda dalam spesifikasi atau rencana pelaksanaan) maka apa yang digunakan tergantung pada keadaan - dan karena Anda tidak menentukan pemilik, sangat mungkin bahwa permintaan adhoc Anda mungkin menggunakan satu dan prosedur yang dikompilasi mungkin menggunakan gunakan yang lain.
Seperti yang saya katakan, itu mungkin tidak mungkin tetapi mungkin dan harus diperiksa karena masalah Anda bisa jadi Anda hanya mencari bug di tempat yang salah.
sumber
Ini mungkin terdengar konyol dan tampak jelas dari nama SessionGUID, tetapi apakah kolom tersebut merupakan pengidentifikasi unik di Report_Opener? Jika tidak, Anda mungkin ingin mencoba melemparkannya ke jenis yang benar dan mencobanya atau mendeklarasikan variabel Anda ke jenis yang benar.
Rencana yang dibuat sebagai bagian dari sproc dapat bekerja secara tidak sengaja dan melakukan pemeran internal di atas meja besar.
sumber
varchar
kolom dengannvarchar
nilai (EgWHERE CustomerName = N'zrendall'
). SQL Server harus mengubah setiap nilai kolom menjadinvarchar
sebelum perbandingan.Saya punya ide lain. Bagaimana jika Anda membuat fungsi berbasis tabel ini:
Dan kemudian dipilih darinya menggunakan pernyataan berikut (bahkan memasukkan ini ke SP Anda):
Sepertinya apa yang terjadi (yang semua orang telah komentari) adalah bahwa SQL Server hanya membuat asumsi di suatu tempat yang salah, dan mungkin ini akan memaksanya untuk memperbaiki asumsi tersebut. Saya benci menambahkan langkah ekstra, tapi saya tidak yakin apa lagi yang menyebabkannya.
sumber
- Ini solusinya:
-- Itu dia
sumber