-2

コミッションレポート用にこのカーソルを書きました。何が起こるかというと、コミッションは 1 つのテーブルにあり、レコードは別のテーブルにあります。特定の基準に基づいて 2 つを一致させます (完全に一致するものはありません)。問題は、レコードが存在する場所に重複があることです。コミッションを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 

T-SQLカーソルを高速化する方法への回答を読みましたか? 、そのため、このクエリを表形式で書き直します。しかし、結合を使用する別のクエリがあり、非常に高速です。問題は、テーブル内の重複を区別できないことrecordsです。

より速くするために私にできることはありますか?これは主な質問です。そうでない場合、それを行う別の方法はありますか。

特に助けが必要です

  • ビューまたはストア プロシージャを使用すると役立ちますか
  • Cursorでキャッシュを使用して高速化する方法があります
  • 構文のその他のオプション
4

2 に答える 2

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;

それでも遅い場合は、おそらくカーソルに問題はありません。次のステップでは、このクエリをより効率的にするために実装できるインデックスを調査します。

于 2012-06-15T17:56:54.587 に答える
0

臨時雇用者はあなたの友達です

2つのテーブルのデータをマージし、複雑な方法で重複を削除し、すべてを非常に高速に行うという問題を解決する方法は、一時テーブルを使用することでした。これは私がしたことです

#tempテーブルを作成し、両方のテーブルからマージされたデータをフェッチします。必要がない場合でも、必ず両方のテーブルにIDフィールドを含めてください。これは、重複を削除するのに役立ちます。

これで、このテーブルであらゆる種類の計算を実行できます。テーブルBから重複を削除し、重複するテーブルBIDを削除するだけです。テーブルAから重複を削除し、重複するテーブルAIdを削除するだけです。問題はもっと複雑ですが、少なくともこれが問題を解決し、カーソルが高すぎて計算にかなりの時間がかかる場合に問題をかなり速くするための最良の方法です。私の場合、+5分かかりました。#tempテーブルは約5秒でクエリを実行しますが、これにはさらに多くの計算が含まれていました。


アーロンソリューションを適用している間、カーソルはそれ以上速くなりませんでした。2番目のクエリは高速でしたが、正しい答えが得られなかったため、最終的に一時テーブルを使用しました。これは私自身の答えです。

于 2012-06-26T14:15:40.550 に答える