SQL Server: Permintaan cepat, tetapi lambat dari prosedur

257

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 StoredProcedureAmeminta StoredProcedureBpanggilan StoredProcedureCpanggilan StoredProcedureDuntuk 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.

Ian Boyd
sumber
Saya perhatikan setiap kali Anda mengambil parameter dan menugaskannya ke yang lain dan kemudian menggunakannya dalam permintaan nanti ini bisa terjadi dan karena jawabannya menyarankan Optimize for @ "someparamname" tidak diketahui dapat bekerja.
JustDave

Jawaban:

405

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:

CREATE PROCEDURE GetOrderForCustomers(@CustID varchar(20))
AS
BEGIN
    SELECT * 
    FROM orders
    WHERE customerid = @CustID
END

Cara cepat:

CREATE PROCEDURE GetOrderForCustomersWithoutPS(@CustID varchar(20))
AS
BEGIN
    DECLARE @LocCustID varchar(20)
    SET @LocCustID = @CustID

    SELECT * 
    FROM orders
    WHERE customerid = @LocCustID
END

Semoga ini bisa membantu orang lain, melakukan ini mengurangi waktu eksekusi saya dari 5+ menit menjadi sekitar 6-7 detik.

Adam Marshall
sumber
23
+1 Tapi, ini sangat aneh, dan memunculkan banyak pertanyaan seperti apakah kita harus melakukan ini untuk semua prosedur dan jika tidak, kapan melakukannya?
Gotqn
31
Apakah saya satu-satunya yang bingung dengan perilaku ini ?? Membutuhkan variabel lokal untuk dideklarasikan untuk mencegah sniffing parameter ?? Bukankah seharusnya SQL Server cukup pintar untuk mencegah hal ini terjadi? Ini hanya menyebabkan kode gembung yang tidak perlu oleh IMHO desain picik Microsoft.
l46kok
4
15 mnt -> 8 dtk! penyelamat hidup
Tony Brix
3
@BennettDill WITH RECOMPILEtidak membuat perbedaan bagi saya, hanya parameter lokal.
mrogers
8
Ini sekarang dapat dicapai dengan menggunakan petunjuk kueri - OPTION (OPTIMIZE FOR (@varA UNKNOWN, @varB UNKNOWN)
Dave
131

Saya menemukan masalahnya, inilah skrip versi lambat dan cepat dari prosedur tersimpan:

dbo.ViewOpener__RenamedForCruachan__Slow.PRC

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS OFF 
GO

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Slow
    @SessionGUID uniqueidentifier
AS

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
GO

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

dbo.ViewOpener__RenamedForCruachan__Fast.PRC

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Fast
    @SessionGUID uniqueidentifier 
AS

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
GO

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

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:

(table.column IS NOT NULL)

Jadi ada beberapa NULL yang terlibat.


Penjelasan lebih lanjut dibuktikan dengan kembali ke Query Analizer, dan berlari

SET ANSI_NULLS OFF

.

DECLARE @SessionGUID uniqueidentifier
SET @SessionGUID = 'BCBA333C-B6A1-4155-9833-C495F22EA908'

.

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank

Dan permintaannya lambat.


Jadi masalahnya bukan karena kueri dijalankan dari prosedur tersimpan. Masalahnya adalah bahwa opsi default koneksi Enterprise Manager ANSI_NULLS off, bukan ANSI_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_NULLSopsi dalam dialog prosedur tersimpan:

Set ANSI_NULLS ON
Go
Create Proc spXXXX as
....
Ian Boyd
sumber
2
Saya masih tidak mengerti bagaimana mengubah ANSI_NULLS ONmembuat perbedaan kinerja yang sangat besar.
Justin Helgerson
2
@ Ek0nomik Karena JOINklausa di dalam tampilan memiliki arti yang berbeda kapan ANSI_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.
Ian Boyd
2
Catatan: ANSI_NULLS OFFsudah usang dan dianggap sebagai praktik buruk
jean
2
tautan "Dalam versi SQL Server yang akan datang, ANSI_NULLS akan selalu AKTIF dan aplikasi apa pun yang secara eksplisit mengatur opsi ke OFF akan menghasilkan kesalahan. Hindari menggunakan fitur ini dalam pekerjaan pengembangan baru, dan berencana untuk memodifikasi aplikasi yang saat ini menggunakan fitur ini. "
sotn
Tidak membantu dalam kasus saya.
st_stefanov
19

Lakukan 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:

EXEC sp_MSforeachtable @command1="print '?' DBCC DBREINDEX ('?', ' ', 80)"
GO
EXEC sp_updatestats
GO 

Semoga ini membantu. Hanya melewati bantuan dari orang lain yang membantu saya.

Carenski
sumber
2
Hanya FYI untuk pembaca masa depan: DBCC REINDEXtelah usang, jadi Anda harus mencari alternatif.
gvee
1
Memperbaiki masalah saya, terima kasih (1m20s ke 2s!). Re : 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."
AjV Jsy
Tidak tahu apakah ini jawaban terbaik tetapi dalam kasus saya sp_updatestats sudah cukup, jadi +1
Todd Menier
..ya dan, jangan lupa bahwa indeks pembangunan kembali mungkin memerlukan waktu dan ruang jadi sebelum Anda menjalankan ini di server produksi Anda pastikan Anda mampu mengatasi kemungkinan pelambatan. Saya akan menyarankan untuk melihat ke REORGANIZE atau REBUILD WITH (ONLINE = ON)
Milan
14

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

Di akhir permintaan Anda, tambahkan OPTION (OPTIMIZE FOR (@now UNKNOWN))

jessieloo
sumber
4

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.

AK
sumber
Menurut entri blog itu, pembekuan rencana hanya untuk MS SQL 2005 dan lebih tinggi, sehingga tidak akan membantu OP.
Coxy
Masalahnya adalah ia menggunakan rencana kueri yang salah. saya tidak ingin membekukannya ke yang salah.
Ian Boyd
4

Saya mengalami masalah ini. Kueri saya terlihat seperti:

select a, b, c from sometable where date > '20140101'

Prosedur tersimpan saya didefinisikan seperti:

create procedure my_procedure (@dtFrom date)
as
select a, b, c from sometable where date > @dtFrom

Saya mengubah tipe data menjadi datetime dan voila! Pergi dari 30 menit hingga 1 menit!

create procedure my_procedure (@dtFrom datetime)
as
select a, b, c from sometable where date > @dtFrom
Lee Tickett
sumber
2
Terima kasih BANYAK Lee, ini menyelamatkan hari saya! Inilah cara saya mendapatkan hanya bagian tanggal bidang datetime: DATEADD (dd, 0, DATEDIFF (dd, 0, table.field))
Julien B.
1
INI Memperbaiki masalah saya. Saya memiliki kolom varchar (20) dan parameter saya adalah nvarchar (50), setelah saya membuat tipe parameter sama dengan tipe kolom - tidak ada lagi penundaan.
st_stefanov
3

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.

AnthonyWJones
sumber
Apakah ada cara untuk menunjukkan dalam deklarasi prosedur tersimpan bahwa parameter i tidak boleh nol? Dan bukankah itu sesuatu yang akan diperbaiki oleh sp_executesql?
Ian Boyd
Dalam kata nggak, bukan pada tahun 2000. 2005 menambahkan petunjuk kueri di mana Anda dapat memberikan nilai contoh untuk parameter, pengoptimal akan mengoptimalkan seolah-olah tahu bahwa parameter selalu digunakan. Setelah mengatakan bahwa saya secara umum menemukan hal semacam ini menjadi masalah statistik.
AnthonyWJones
Jika masalah statistik, mereka berfungsi dengan baik dari QA ketika saya menjalankannya ad-hoc, sp_executesql, exec (). Dan mengapa mereka semua kemudian berjalan dengan buruk ketika prosedur tersimpan berisi ad-hoc sql, sp_executesql, exec ()?
Ian Boyd
1

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?

SqlRyan
sumber
Petunjuk kueri paling berguna dalam referensi itu tidak tersedia di SQL 2000 yang merupakan versi yang dipermasalahkan di sini.
AnthonyWJones
Selain itu, petunjuk apa yang dibutuhkan? SQL Server mampu mengetahuinya tidak masalah ketika saya menjalankannya ad-hoc.
Ian Boyd
Tentu, dan itu selalu menjadi pengalaman saya juga. Namun, dalam hal ini, dia mengatakan itu datang dengan rencana eksekutif yang sama sekali berbeda. Mungkin ada indeks yang digunakan ad-hoc, tetapi untuk beberapa alasan diabaikan di proc. Dia dapat memaksa SQL Server untuk menggunakan indeks dengan petunjuk "INDEX".
SqlRyan
1

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

sp_recompile ViewOpener

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.

Cruachan
sumber
1

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.

David Rendall
sumber
Bukan itu. Tetapi saya telah melihat masalah kinerja dengan klausa mana yang membandingkan varcharkolom dengan nvarcharnilai (Eg WHERE CustomerName = N'zrendall'). SQL Server harus mengubah setiap nilai kolom menjadi nvarcharsebelum perbandingan.
Ian Boyd
0

Saya punya ide lain. Bagaimana jika Anda membuat fungsi berbasis tabel ini:

CREATE FUNCTION tbfSelectFromView
(   
    -- Add the parameters for the function here
    @SessionGUID UNIQUEIDENTIFIER
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT *
    FROM Report_Opener
    WHERE SessionGUID = @SessionGUID
    ORDER BY CurrencyTypeOrder, Rank
)
GO

Dan kemudian dipilih darinya menggunakan pernyataan berikut (bahkan memasukkan ini ke SP Anda):

SELECT *
FROM tbfSelectFromView(@SessionGUID)

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.

SqlRyan
sumber
0

- Ini solusinya:

create procedure GetOrderForCustomers(@CustID varchar(20))

as

begin

select * from orders

where customerid = ISNULL(@CustID, '')

end

-- Itu dia

Koldoon
sumber