Mengapa kueri berjalan lebih lambat dalam Prosedur Disimpan daripada di jendela Kueri?


14

Saya memiliki kueri kompleks yang berjalan dalam 2 detik di jendela kueri, tetapi sekitar 5 menit sebagai Prosedur Tersimpan. Mengapa perlu waktu lebih lama untuk berjalan sebagai prosedur tersimpan?

Begini tampilannya.

Dibutuhkan serangkaian catatan khusus (diidentifikasi oleh @iddan @createdDate), dan kerangka waktu tertentu (1 tahun mulai dari @startDate) dan mengembalikan daftar surat yang dirangkum yang dikirim dan perkiraan pembayaran yang diterima sebagai hasil dari surat-surat itu.

CREATE PROCEDURE MyStoredProcedure
    @id int,
    @createdDate varchar(20),
    @startDate varchar(20)

 AS
SET NOCOUNT ON

    -- Get the number of records * .7
    -- Only want to return records containing letters that were sent on 70% or more of the records
    DECLARE @limit int
    SET @limit = IsNull((SELECT Count(*) FROM RecordsTable WITH (NOLOCK) WHERE ForeignKeyId = @id AND Created = @createdDate), 0) * .07

    SELECT DateSent as [Date] 
        , LetterCode as [Letter Code]
        , Count(*) as [Letters Sent]
        , SUM(CASE WHEN IsNull(P.DatePaid, '1/1/1753') BETWEEN DateSent AND DateAdd(day, 30, DateSent) THEN IsNull(P.TotalPaid, 0) ELSE 0 END) as [Amount Paid]
    INTO #tmpTable
    FROM (

        -- Letters Table. Filter for specific letters
        SELECT DateAdd(day, datediff(day, 0, LR.DateProcessed), 0) as [DateSent] -- Drop time from datetime
            , LR.LetterCode -- Letter Id
            , M.RecordId -- Record Id
        FROM LetterRequest as LR WITH (NOLOCK)
        INNER JOIN RecordsTable as M WITH (NOLOCK) ON LR.RecordId = M.RecordId
        WHERE ForeignKeyId = @id AND Received = @createdDate
            AND LR.Deleted = 0 AND IsNull(LR.ErrorDescription, '') = ''
            AND LR.DateProcessed BETWEEN @startDate AND DateAdd(year, 1, @startDate)
            AND LR.LetterCode IN ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o')
    ) as T
    LEFT OUTER JOIN (

        -- Payment Table. Payments that bounce are entered as a negative payment and are accounted for
        SELECT PH.RecordId, PH.DatePaid, PH.TotalPaid
        FROM PaymentHistory as PH WITH (NOLOCK)
            INNER JOIN RecordsTable as M WITH (NOLOCK) ON PH.RecordId = M.RecordId
            LEFT OUTER JOIN PaymentHistory as PR WITH (NOLOCK) ON PR.ReverseOfUId = PH.UID
        WHERE PH.SomeString LIKE 'P_' 
            AND PR.UID is NULL 
            AND PH.DatePaid BETWEEN @startDate AND DateAdd(day, 30, DateAdd(year, 1, @startDate))
            AND M.ForeignKeyId = @id AND M.Created = @createdDate
    ) as P ON T.RecordId = P.RecordId

    GROUP BY DateSent, LetterCode
    --HAVING Count(*) > @limit
    ORDER BY DateSent, LetterCode

    SELECT *
    FROM #tmpTable
    WHERE [Letters Sent] > @limit

    DROP TABLE #tmpTable

Hasil akhirnya terlihat seperti ini:

Tanggal Surat Kode Surat Dikirim Jumlah Dibayar
1/1/2012 a 1245 12345,67
1/1/2012 b 2301 1234.56
1/1/2012 c 1312 7894.45
1/1/2012 a 1455 2345,65
1/1/2012 c 3611 3213.21

Saya mengalami masalah mencari tahu di mana perlambatannya, karena semuanya berjalan sangat cepat di editor kueri. Ini hanya ketika saya memindahkan kueri ke prosedur tersimpan yang mulai memakan waktu lama untuk dijalankan.

Saya yakin itu ada hubungannya dengan rencana eksekusi permintaan yang dihasilkan, tetapi saya tidak cukup tahu tentang SQL untuk mengidentifikasi apa yang dapat menyebabkan masalah.

Mungkin harus dicatat bahwa semua tabel yang digunakan dalam kueri memiliki jutaan catatan.

Dapatkah seseorang menjelaskan kepada saya mengapa ini membutuhkan waktu lebih lama untuk berjalan sebagai prosedur tersimpan daripada di editor kueri, dan membantu saya mengidentifikasi bagian mana dari kueri saya yang dapat menyebabkan masalah kinerja ketika dijalankan sebagai prosedur tersimpan?


@MartinSmith Terima kasih. Saya lebih suka menghindari RECOMPILEpetunjuk itu karena saya tidak benar-benar ingin mengkompilasi ulang permintaan setiap kali dijalankan, dan artikel yang Anda tautkan menyebutkan bahwa menyalin parameter ke variabel lokal sama dengan menggunakan OPTIMIZE FOR UNKNOWN, yang tampaknya hanya tersedia di 2008 dan selanjutnya. Saya pikir untuk saat ini saya akan tetap dengan menyalin parameter ke variabel lokal, yang membawa waktu eksekusi permintaan saya turun kembali ke 1-2 detik.
Rachel

Jawaban:


5

Seperti Martin tunjukkan dalam komentar , masalahnya adalah bahwa kueri menggunakan rencana cache yang tidak pantas untuk parameter yang diberikan.

Tautan yang dia berikan pada Slow in the Application, Fast in SSMS? Memahami Misteri Kinerja memberikan banyak informasi berguna yang menuntun saya ke beberapa solusi.

Solusi yang saya gunakan saat ini adalah menyalin parameter ke variabel lokal dalam prosedur, yang saya pikir membuat SQL mengevaluasi kembali rencana eksekusi untuk permintaan kapan saja itu dijalankan, sehingga ia mengambil rencana eksekusi terbaik untuk parameter yang diberikan daripada menggunakan rencana cache yang tidak pantas untuk kueri.

Solusi lain yang mungkin berfungsi adalah menggunakan petunjuk OPTIMIZE FORatau RECOMPILEkueri.


0

Dari pertanyaan serupa di Stackoverflow ( dengan lebih banyak jawaban ), periksa prosedur tersimpan Anda.

  • BURUK :SET ANSI_NULLS OFF (5 menit, spool bersemangat 6 juta)
  • BAIK :SET ANSI_NULLS ON (0,5 detik)
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.