-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

临时表是你的朋友

我解决问题的方法,合并两个表中的数据,以复杂的方式删除重复项,一切都非常快,就是使用临时表。这就是我所做的

创建一个#temp 表,从两个表中获取合并的数据。确保在两个表中都包含 ID 字段,即使您不需要它。这将有助于删除重复项。

现在你可以在这张表上进行各种计算。从表 B 中删除重复项,只需删除重复的表 B ID。从表 A 中删除重复项,只需删除重复的表 A Id。这个问题有更多的复杂性,但至少这可能是解决问题的最佳方法,如果游标太昂贵并且需要相当长的时间来计算,它会大大加快速度。就我而言,它需要+ 5分钟。#temp 表查询大约 5 秒,其中包含更多计算。


在应用 Aaron 解决方案时,光标并没有变得更快。第二个查询更快,但它没有给我正确的答案,所以最后我使用了临时表。这是我自己的答案。

于 2012-06-26T14:15:40.550 回答