2012-06-15 1 views
-2

위탁 보고서에이 커서를 작성했습니다. 커미션이 한 테이블에오고 레코드는 다른 테이블입니다. 나는 특정 critera에 근거한 2 개를 match한다 (정확한 match가 없다). 문제는 레코드가있는 중복이 있다는 것입니다. 커미션을 records 테이블과 일치 시키면 이러한 중복을 가져올 수 있습니다. 따라서 담당자는 더 많은 돈을받습니다. 다른 한편 수수료 표에도 중복 기록이 있지만 계정이 2 개월 동안 지불되었음을 의미하는 것은 간단합니다.커서를 빠르게 만드는 방법

이 쿼리를 작성했지만 실행하는 데 5 분 이상이 걸립니다. 레코드 테이블에는 50,000 개의 레코드가 있고 커미션 테이블에는 100,000 개의 레코드가 있습니다. 이 커서를 개선하는 방법이 있습니까?

/* just preparation of cursor, this is not time consuming */ 
CREATE TABLE #result 
    (
    repid   INT, 
    AccountNo  VARCHAR(100), 
    supplier  VARCHAR(15), 
    CompanyName VARCHAR(200), 
    StartDate  DATETIME, 
    EndDate  DATETIME, 
    Product  VARCHAR(25), 
    commodity  VARCHAR(25), 
    ContractEnd DATETIME, 
    EstUsage  INT, 
    EnrollStatus VARCHAR(10), 
    EnrollDate DATETIME, 
    ActualEndDate DATETIME, 
    MeterStart DATETIME, 
    MeterEnd  DATETIME, 
    ActualUsage INT 
) 

DECLARE @AccountNo VARCHAR(100) 
DECLARE @supplier VARCHAR(10) 
DECLARE @commodity VARCHAR(15) 
DECLARE @meterstart DATETIME 
DECLARE @meterEnd DATETIME 
DECLARE @volume FLOAT 
DECLARE @RepID INT 
DECLARE @Month INT 
DECLARE @Year INT 

SET @repID = 80 
SET @Month = 1 
SET @year = 2012 

/* the actual cursor */ 
DECLARE commission_cursor CURSOR FOR 
    SELECT AccountNo, 
     supplier, 
     commodity, 
     meterStart, 
     MeterEnd, 
     Volume 
    FROM commission 
    WHERE Datepart(m, PaymentDate) = @Month 
     AND Datepart(YYYY, PaymentDate) = @Year 

OPEN commission_cursor 

FETCH next FROM commission_cursor INTO @AccountNo, @supplier, @commodity, @MeterStart, @MeterEnd, @Volume; 

WHILE @@fetch_status = 0 
    BEGIN 
     IF EXISTS (SELECT id 
       FROM Records 
       WHERE AccountNo = @AccountNo 
         AND supplier = @supplier 
         AND Commodity = @commodity 
         AND RepID = @repID) 
     INSERT INTO #result 
     SELECT TOP 1 RepID, 
        AccountNo, 
        Supplier, 
        CompanyName, 
        [Supplier Start Date], 
        [Supplier End Date], 
        Product, 
        Commodity, 
        [customer end date], 
        [Expected Usage], 
        EnrollStatus, 
        ActualStartDate, 
        ActualEndDate, 
        @meterstart, 
        @MeterEnd, 
        @volume 
     FROM Records 
     WHERE AccountNo = @AccountNo 
       AND supplier = @supplier 
       AND Commodity = @commodity 
       AND RepID = @repID 
       AND @MeterStart >= Dateadd(dd, -7, ActualStartDate) 
       AND @meterEnd <= Isnull(Dateadd(dd, 30, ActualEndDate), '2015-12-31') 

     FETCH next FROM commission_cursor INTO @AccountNo, @supplier, @commodity, @MeterStart, @MeterEnd, @Volume; 
    END 

SELECT * 
FROM #result 

/* clean up */ 
CLOSE commission_cursor 

DEALLOCATE commission_cursor 

DROP TABLE #result 

나는 내가 무엇을 얻을 것은 테이블 형태로이 쿼리를 다시 작성입니다 위해, How to make a T-SQL Cursor faster?에 대답을 읽었습니다. 하지만 조인을 사용하고 번개가 빠른 또 다른 쿼리가 있습니다. 문제는, 내 records 테이블에있는 dups를 구별 할 수 없다는 것입니다.

제가 할 수있는 일이 더 빠릅니까? 이것은 기본적인 질문입니다. 그렇지 않다면, 그것을 할 대안이 있습니까?

은 내가 특별히가

  • 내가 커서에서 캐시를 사용할 수있는 방법이 만들어

    • 하는 뷰 또는 저장 프로 시저의 도움을 사용하는 것입니다 도움이 필요보다 빠르고
    • 구문에서 다른 옵션
  • +2

    링크 된 질문의 커서가'FAST_FORWARD' 옵션으로 선언 된 것을 알고 계셨습니까? –

    +0

    그게 무슨 뜻인지 모르겠다. 그는 아마도 여기에 적용되지 않는 커서에서 커서를 사용하고있었습니다. –

    +3

    커서 성능의 주요 문제점은 기본 옵션 (전역, 동적, 읽기/쓰기, 스크롤 가능)이 사용될 때입니다. 이 기사를 읽으십시오 : http://sqlblog.com/blogs/aaron_bertrand/archive/2012/01/26/the-fallacy-that-a-while-loop-isn-ta-cursor.aspx 그 뜻은 아닙니다. 'LOCAL STATIC READ_ONLY FORWARD_ONLY'을 추가하는 것은 효율성을 향상시키는 * 유일한 방법입니다 (루프없이이 작업을 수행 할 수 있습니다). 그러나 좋은 시작입니다. –

    답변

    8

    첫 번째 옵션은 커서에 대해 리소스를 가장 적게 사용하는 옵션을 설정하는 것입니다.

    declare commission_cursor cursor 
    local static read_only forward_only 
    for 
    

    다음은 커서가 필요한지 여부를 조사하는 것입니다. 이 경우 당신이 단일 패스없이 루프와 동일한 기능을 수행 할 수 있다고 생각이 여전히 느린 경우

    ;WITH x AS 
    (
        SELECT 
        rn = ROW_NUMBER() OVER (PARTITION BY r.AccountNo, r.Supplier, r.Commodity, r.RepID 
         ORDER BY r.ActualEndDate DESC), 
        r.RepID, 
        r.AccountNo, 
        r.Supplier, 
        r.CompanyName, 
        StartDate = r.[Supplier Start Date], 
        EndDate = r.[Supplier End Date], 
        r.Product, 
        r.Commodity, 
        ContractEnd = r.[customer end date], 
        EstUsage = r.[Expected Usage], 
        r.EnrollStatus, 
        EnrollDate = r.ActualStartDate, 
        r.ActualEndDate, 
        c.MeterStart, 
        c.MeterEnd, 
        ActualUsage = c.Volume 
        FROM dbo.commission AS c 
        INNER JOIN dbo.Records AS r 
        ON c.AccountNo = r.AccountNo 
        AND c.Supplier = r.Supplier 
        AND c.Commodity = r.Commodity 
        AND c.RepID = r.RepID 
        WHERE 
        c.PaymentDate >= DATEADD(MONTH, @Month-1, CONVERT(CHAR(4), @Year) + '0101') 
        AND c.PaymentDate < DATEADD(MONTH, 1, CONVERT(CHAR(4), @Year) + '0101') 
        AND r.RepID = @RepID 
    ) 
    SELECT RepID, AccountNo, Supplier, CompanyName, StartDate, EndDate, 
        Product, Commodity, ContractEnd, EstUsage, EnrollStatus, EnrollDate, 
        ActualEndDate, MeterStart, MeterEnd, ActualUsage 
    FROM x 
    WHERE rn = 1 --ORDER BY something; 
    

    , 다음 커서 아마 문제가 아니었다 - 다음 단계는 조사됩니다 어떤 인덱스를 수도 이 쿼리를보다 효율적으로 만들 수 있도록 구현해야합니다.

    +1

    +1 [커서는 악마입니다] (https://www.google.com/search?hl=ko&q=devil+cursors&bav=on.2,or.r_gc.r_pw.r_qf.,cf.osb&biw=1920&bih=956&wrapid = tlif133978342923310 & um = 1 & ie = UTF-8 & tbm = isch & source = og & sai = DHnbT5LKJ4Ks2gWGoJDMCA # um = 1 & hl = en & gt = 악마 + 아이콘 & 악마 = 아이콘 & aqi = g2g-m3g-S4g-msS1 & aql = & gs_l = img .3..0l2j0i5l3j0i24l4j0i5i10i24.9679.12090.0.12184.18.11.2.0.0.2.130.1039.4j6.10.0 ... 0.0.PzsA8FZMrBI & bav = on.2 또는 .r_gc.r_pw.r_qf., cf.osb & fp = 36f0bd954406024d & biw = 1920 & bih = 956), 일반적인 SQL 문과 함께있을 가능성이 매우 높습니다. –

    +0

    이 쿼리는 거의 즉시 실행되지만 9000 개의 레코드를 반환합니다.이 레코드는 400 개만 반환됩니다.이 작업이 필요합니다. –

    +0

    죄송합니다. 커서 안의 where 절에서 @RepID 변수를 놓쳤을뿐입니다 (마틴이 고치기 전에 서식을 잃어버린 것 같습니다). 내 업데이트 된 코드를 사용해보십시오. 첫 번째 시도는 커서가 아니어야합니다. 사용하지 않아야하는지에 대해 조금 덜 강하게 느낍니다. 즉, 집계가있는 등 총계가 있습니다. –

    0

    임시 테이블은 당신의 친구입니다

    나는 두 테이블에서 데이터를 병합, 내 문제를 해결 복잡한 방식으로 중복을 제거하고 모든 매우 빠른 임시 테이블을 사용하는 것이었다 방법. 이것이 내가 한 것입니다.

    #temp 테이블을 생성하고 두 테이블에서 병합 된 데이터를 가져옵니다. 필요하지 않더라도 두 테이블 모두에 ID 필드를 포함하십시오. 이렇게하면 중복을 제거하는 데 도움이됩니다.

    이제이 테이블에서 모든 종류의 계산을 수행 할 수 있습니다. 테이블 B에서 중복을 제거하고, 중복 테이블 B ID를 제거하십시오. 테이블 A에서 중복을 제거하고 중복 테이블 A ID를 제거하십시오. 문제의 복잡성은 더 커지지 만 적어도 커서를 너무 비싸게 계산하는 데 시간이 오래 걸리면 문제를 해결하고이를 훨씬 빠르게 수행하는 가장 좋은 방법 일 것입니다. 내 경우에는 +5 분이 걸렸다. 약 5 초 정도 #temp 테이블 쿼리에 더 많은 계산이 포함되어 있습니다.


    아론 솔루션을 적용하는 동안 커서가 더 빨리 움직이지 않았습니다. 두 번째 쿼리는 빠르지 만 정답을주지 못하므로 결국 임시 테이블을 사용했습니다. 이것은 내 자신의 대답이다.