2016-02-13 182 views
0

我有一個存儲過程使用while循環臨時表和光標,通過它我得到了客戶的老化平衡,但是我的SP工作正常,但我有一些性能問題,因爲它需要15秒從一小塊數據產生結果。我正在尋找一種更有效的方式來做到這一點。存儲過程的性能問題

在此先感謝。

這是我的存儲過程。

CREATE TABLE #Customer_Temp (
    AccountCode varchar(50), 
    AccountTitle varchar(50), 
    CurrentBalance int, 
    FirstBalance int, 
    SecondBalance int, 
    ThirdBalance int, 
    FourthBalance int, 
    FifthBalance int, 
    SixthBalance int, 
    SeventhBalance int, 
    EighthBalance int, 
    OpeningBalance int 
) 

INSERT INTO #customer_temp (AccountCode, AccountTitle, OpeningBalance) 
    SELECT 
    Customer.AccountCode, 
    Customer.Name, 
    COA.OpeningBalance 
    FROM Customers AS Customer 
    INNER JOIN ChartOfAccount AS COA 
    ON COA.CompanyId = @Companyid 
    AND COA.BusinessUnitId = @BusinessUnitId 
    AND COA.ChartAccount = Customer.AccountCode 

--Create Table And Duplicate Customers Data In it ENDED 

DECLARE @DrAmount AS int 
DECLARE @CrAmount AS int 
DECLARE @Balance AS int 
DECLARE @FBalance AS int 
DECLARE @SBalance AS int 
DECLARE @TBalance AS int 
DECLARE @FoBalance AS int 
DECLARE @FIBalance AS int 
DECLARE @SIBalance AS int 
DECLARE @SEBalance AS int 
DECLARE @EBalance AS int 

DECLARE @FSDate AS date 
DECLARE @FLDate AS date 
DECLARE @SSDate AS date 
DECLARE @SLDate AS date 
DECLARE @TSDate AS date 
DECLARE @TLDate AS date 
DECLARE @FOSDate AS date 
DECLARE @FOLDate AS date 
DECLARE @FISDate AS date 
DECLARE @FILDate AS date 
DECLARE @SISDate AS date 
DECLARE @SILDate AS date 
DECLARE @SESDate AS date 
DECLARE @SELDate AS date 
DECLARE @ESDate AS date 

SET @FSDate = DATEADD(DAY, -1, @StartDate) 
SET @FLDate = DATEADD(DAY, -6, @FSDate) 

SET @SSDate = DATEADD(DAY, -1, @FLDate) 
SET @SLDate = DATEADD(DAY, -6, @SSDate) 

SET @TSDate = DATEADD(DAY, -1, @SLDate) 
SET @TLDate = DATEADD(DAY, -14, @TSDate) 

SET @FOSDate = DATEADD(DAY, -1, @TLDate) 
SET @FOLDate = DATEADD(DAY, -14, @FOSDate) 

SET @FISDate = DATEADD(DAY, -1, @FOLDate) 
SET @FILDate = DATEADD(DAY, -14, @FISDate) 

SET @SISDate = DATEADD(DAY, -1, @FILDate) 
SET @SILDate = DATEADD(DAY, -29, @SISDate) 

SET @SESDate = DATEADD(DAY, -1, @SILDate) 
SET @SELDate = DATEADD(DAY, -89, @SESDate) 

SET @ESDate = DATEADD(DAY, -1, @SELDate) 

DECLARE @TempCCode AS varchar(50) 
DECLARE @TempOBalance AS float 

DECLARE CustomerCursor CURSOR FOR 
SELECT 
    AccountCode, 
    OpeningBalance 
FROM #Customer_Temp 

OPEN CustomerCursor 
FETCH NEXT FROM CustomerCursor INTO @TempCCode, @TempOBalance 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    EXEC @FBalance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @FSDate, 
              @FLDate, 
              @Fyear 

    EXEC @SBalance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @SSDate, 
              @SLDate, 
              @Fyear 

    EXEC @TBalance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @TSDate, 
              @TLDate, 
              @Fyear 

    EXEC @FoBalance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @FOSDate, 
              @FOLDate, 
              @Fyear 

    EXEC @FIBalance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @FISDate, 
              @FILDate, 
              @Fyear 

    EXEC @SIBalance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @SISDate, 
              @SILDate, 
              @Fyear 

    PRINT @SESDate 
    PRINT @SELDate 
    EXEC @SEBalance = 
    GetBalanceOfAgingOnDate @BusinessUnitId, 
          @Companyid, 
          @TempCCode, 
          @SESDate, 
          @SELDate, 
          @Fyear 

    EXEC @EBalance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @ESDate, 
              @EndDate, 
              @Fyear 

    EXEC @Balance = GetBalanceOfAgingOnDate @BusinessUnitId, 
              @Companyid, 
              @TempCCode, 
              @StartDate, 
              @EndDate, 
              @Fyear 
    UPDATE #Customer_Temp 
    SET CurrentBalance = (@Balance + @TempOBalance), 
     FirstBalance = @FBalance, 
     SecondBalance = @SBalance, 
     ThirdBalance = @TBalance, 
     FourthBalance = @FoBalance, 
     FifthBalance = @FIBalance, 
     SixthBalance = @SIBalance, 
     SeventhBalance = @SEBalance, 
     EighthBalance = @EBalance 
    WHERE AccountCode = @TempCCode 
    FETCH NEXT FROM CustomerCursor INTO @TempCCode, @TempOBalance 
END 

CLOSE CustomerCursor 
DEALLOCATE CustomerCursor 

,這裏是在光標稱爲存儲過程

CREATE PROCEDURE [dbo].[GetBalanceOfAgingOnDate] 
@BusinessUnitId int, 
@Companyid int, 
@ChartAccount as varchar (50), 
@StartDate as DateTime, 
@EndDate as DateTime, 
@Fyear as varchar(50) 
AS BEGIN 

Declare @DrAmount as int 
Declare @CrAmount as int 
Declare @Balance as int 

set @DrAmount=(select sum(Dr_Amount) from AccountVocherMaster AS AM , 
AccountVocherChild AS AC Where AM.CompanyId = @Companyid AND 
AM.BusinessUnitId = @BusinessUnitId AND AM.FYear = @Fyear AND 
AM.VocherId = AC.VocherId AND [email protected] AND 
AC.CreatedOn Between @EndDate AND @StartDate); 

set @CrAmount=(select sum(Cr_Amount) from AccountVocherMaster AS AM , 
AccountVocherChild AS AC Where AM.CompanyId = @Companyid AND 
AM.BusinessUnitId = @BusinessUnitId AND AM.FYear = @Fyear AND 
AM.VocherId = AC.VocherId AND [email protected] AND 
AC.CreatedOn Between @EndDate AND @StartDate); 

set @Balance = @DrAmount - @CrAmount ; 

return ISNULL(@Balance,0) 

END 
+1

這很明顯,擺脫了光標和程序。以基於集合的方式處理它,而不是基於行。 –

+0

由於詹姆斯躲過了,循環和遊標通常很慢。此外,如果不知道這些存儲過程是如何寫入的,則不能排除這些存儲過程成爲瓶頸。也許你可以擴展你正在努力完成的任務,如果沒有這個背景,這可能是一個非常大的討論,涉及逐個拆開你的查詢。 – Nicarus

+0

@Nicarus我已經添加了被調用的存儲過程中的問題,現在請幫助我 問候 – faizzyy

回答

0

你的問題是,你不認爲SQL和自作聰明 - 你寫的程序代碼,SQL Server必須執行一個由INSTEAD在集合中工作,並讓查詢優化器找出如何最有效地做到這一點。

遊標和程序通常是緩慢的元素。儘可能少的原子SQL語句,儘管這些語句是一兩頁或更長的頁面,但最好還是使用這種簡單的SQL語句來實現它。然後查詢優化器可以找出如何最有效地實現這個結果。

就你而言,第二個SP在光標中被重複調用 - 有可能有更好的方法來實現這一點。在一個或很少的電話。

0

希望這可以啓動你的道路。你的問題有很多,所以我可能不會(a)在這裏理解所有的東西,(b)解決每一個部分,但是希望你可以從這裏拿走它。我沒有看到任何程序或循環的需要。試試這個,我評論一些上下文:

/* Construct a temp table to store all balance dates (and respective names) */ 
CREATE TABLE #BalanceDates (BalanceName VARCHAR(20), StartDate DATETIME, EndDate DATETIME); 

INSERT INTO BalanceDates VALUES ('FirstBalance',DATEADD(DAY,-1,@StartDate),DATEADD(DAY,-6,@StartDate)); 
-- And so on with remaining inserts... 

/* Create a denormalized table of all balances */ 
SELECT 
    [FirstBalance], 
    [SecondBalance], 
    ... 
INTO 
    #Balances 
FROM 
(
SELECT 
    BD.BalanceName, 
    ISNULL(SUM(Dr_Amount) - SUM(Cr_Amount),0) AS BalanceAmount 
FROM 
    #BalanceDates BD 
LEFT JOIN 
    AccountVocherChild AC 
    ON (AC.CreatedOn BETWEEN BD.StartDate AND BD.EndDate) 
LEFT JOIN 
    AccountVocherMaster AM 
    ON (AM.VocherId = AC.VocherId) 
WHERE 
    AM.CompanyId = @Companyid AND 
    AM.BusinessUnitId = @BusinessUnitId AND 
    AM.FYear = @Fyear AND 
    AC.AccountCode = @ChartAccount 
GROUP BY 
    BD.BalanceName 
) data 
PIVOT 
(
AVG(BalanceAmount) 
FOR BalanceName IN ([FirstBalance],[SecondBalance],...) 
) pvt; 

/* Update the table accordingly */ 
UPDATE tgt 
SET 
    FirstBalance = src.FirstBalance, 
    SecondBalance = src.SecondBalance, 
    ... 
FROM #Customer_Temp tgt 
JOIN #Balances src 
ON (1 = 1); 
1
  • 化妝光標一個局部變量DECLARE @CustomerCursor CURSOR,確保它不是動態的,並不能反映更新光標的源表SET FOR @CustomerCursor = CURSOR FAST_FORWARD,在通過當前光標位置的端更新,避免額外的搜索UPDATE SET #Customer_Temp ... WHERE作者@CustomerCursor
  • CURRENT由單個塞萊獲得類似的條件不同的列的聚集體CT 選擇@DrAmount = SUM(Dr_Amount),@CrAmount = SUM(Cr_Amount) 更多的是:選擇@Balance = SUM(Dr_Amount) - 和(Cr_Amount)
  • 避免古式用逗號連接,寫入內部或外部連接,將連接條件放入ON子句中,將篩選放入WHERE子句
  • 避免將SQL視爲PHP或Pascal等常規編程語言;它以結果爲導向;嘗試使用INLINE TABLE FUNCTION而不是過程(GetBalanceOfAgingOnDate) - 這將使您能夠加入到您的查詢中; 提示:不要使它成爲標量值或表值函數。看看你的聚合sp和它的使用方式。對於每一行(公司),您一次又一次地調用該sp,並在其中彙總一些值。爲什麼不去GROUP BY公司和BusinessUnitID,從AccountVocherMaster運行一個選擇並將其加入到#Customer_Temp?爲什麼不通過單個查詢彙總不同時期的摘要?再看一下:你有一個公司列表,一個聚合查詢,提供一些由公司分組的概要值......爲什麼你仍然有一個遊標?這是一個查詢作業
  • 如果您有大量數據並且按日期進行過濾是使其運行速度快的唯一方法 - 請不要使用單個查詢進行10次查詢。這反正會比10 * 2 * [N個公司]要好得多。實際上,所需的時間已經確定 - 最大範圍是[@ StartDate-180,@StartDate] - 並且它看起來像單個查詢再次是一個好主意。在某些情況下,如果您的數據非常大,則使用標量值進行遊標和過濾可能仍然是改進性能的一個很好的改變,因此您可能仍然希望通過單個CompanyID進行過濾 - 但是,您彙總的數據仍位於在相同的兩個表中的固定日期範圍內 - AccountVocherMaster和AccountVocherChild;光標內通過公司循環,跑單聚集查詢
  • 是絕對沒有在你的GetBalanceOfAgingOnDate的來源聰明的 - 不知道爲什麼你把它作爲一個獨立的模塊
  • 必須有它周圍
交易