【问题标题】:select query in Cursor taking too long在光标中选择查询花费太长时间
【发布时间】:2014-09-10 14:26:00
【问题描述】:

我设计了一个游标来针对 6500 名检查员运行一些统计信息,但花费的时间太长。游标中还有许多其他选择查询,但它们运行正常,但以下选择运行非常缓慢。没有光标选择查询运行得很好。

要求:

访问已上传文件的每个检查员的访问次数(1 次或 2 次或 13 次)

表格:

  • Inspectors: InspectorID
  • InspectionScope: ScopeID, InspectorID (FK)
  • Visits: VisitID, VisitDate ScopeID (FK)
  • VisitsDoc: DocID, DocType, VisitID (FK)

光标代码:

DECLARE 
        @curInspID int, 
        @DateFrom date, @DateTo date;

SELECT @DateTo = CAST(GETDATE() AS DATE)
       ,@DateFrom = CAST(GETDATE() - 90 AS DATE)


DECLARE  
        @InspectorID int,
        @TotalVisits int;


DECLARE @Report TABLE (
        InspectorID int,
        TotalVisits int)


DECLARE curList CURSOR FOR
    SELECT InspectorID FROM Inspectors ;        


OPEN curList
FETCH NEXT FROM curList INTO @curInspID;

WHILE @@FETCH_STATUS = 0
BEGIN

SELECT 
    @curInspID = s.InspectorID    
    ,@TotalVisits = COUNT(distinct v.visitID)
from Visits v 
inner join InspectionScope s on s.ScopeID = v.ScopeID 
inner join VisitDocs vd on vd.VisitID = v.VisitID 
where s.InspectorID = @curInspID and vd.DocType IN (1,2,13) 
and v.VisitDate BETWEEN @DateFrom and @DateTo
group by s.InspectorID 


INSERT INTO @Report VALUES(@curInspID,@TotalVisits);

FETCH NEXT FROM curList INTO @curInspID;
END

CLOSE curList
DEALLOCATE curList

SELECT * FROM @Report

以下查询在同一光标内运行正常

    ,@TotalVisitsWithReportScore = (select COUNT(v.visitid) from visits v
                        inner join InspectionScope s on s.ScopeID = v.ScopeID 
                        where v.ReportStandard not in (0,9) and v.VisitType = 1
                            and v.VisitDate BETWEEN @DateFrom and @DateTo
                            and s.InspectorID = @curInspID 
                            )

    ,@TotalVisitsWith_ReportScore_RejectionFeedBack = (select COUNT(v.visitid) from visits v
                        inner join InspectionScope s on s.ScopeID = v.ScopeID 
                        where v.ReportStandard not in (0,9) and v.VisitType = 1
                            and v.DiscrepancyType IN (2,5,6,7,8)
                            and v.VisitDate BETWEEN @DateFrom and @DateTo
                            and s.InspectorID = @curInspID 
                    )

【问题讨论】:

  • 我之前在使用游标运行查询时遇到过问题。如果您在中途取消它,它会将打开的游标保留在内存中。
  • 请澄清一下 - 与相同参数选择但在光标外部进行比较的光标内的单个选择运行缓慢,还是此选择的总共 6500 次迭代比您预期的要慢?跨度>
  • @Andy,使用相同的参数选择外部光标不到 8 秒,但在光标内部需要超过 1 分钟。在同一个游标中还有许多其他选择查询运行良好,但为了使这个示例更短,我包含了一个运行缓慢的查询。我已经用其他几个运行正常的查询更新了我的问题

标签: sql sql-server-2008 tsql


【解决方案1】:

这里不需要光标——您可以使用INSERT INTOSELECT,加入Inspector 表。

INSERT INTO @Report 
SELECT 
    s.InspectorID    
    , COUNT(distinct v.visitID)
from Visits v 
    inner join InspectionScope s on s.ScopeID = v.ScopeID 
    inner join VisitDocs vd on vd.VisitID = v.VisitID 
    inner join Inspector i on s.InspectorID = i.InspectorId 
where vd.DocType IN (1,2,13) 
and v.VisitDate BETWEEN @DateFrom and @DateTo
group by s.InspectorID 

请注意,如果 Inspector 表中存在其他表中不存在的结果,则您可能需要将 OUTER JOIN 与表一起使用。取决于您的数据和期望的结果。

【讨论】:

  • 我已经使用了 INSERT INTO 和 SELECT 但想知道为什么在游标内运行时它太慢了。
  • @user1263981 - 游标非常慢。一般来说,尽量远离他们。使用游标时,您将多次运行 select/insert,检查器表中的每条记录 1 次。因此,如果您的检查器表中有 100 条记录,那就是 200 次 sql 调用!这只运行一次。
【解决方案2】:

加快光标速度的最佳方法是.... 摆脱它

在这里,您绝对不需要光标 - 一个简单的 SELECT 就可以了 - 而且应该明显更快!

DECLARE @Report TABLE (InspectorID int, TotalVisits int)

DECLARE curList CURSOR FOR
    SELECT InspectorID FROM Inspectors ;        


OPEN curList
FETCH NEXT FROM curList INTO @curInspID;

WHILE @@FETCH_STATUS = 0
BEGIN

INSERT INTO @Report (InspectorID, TotalVisits)
   SELECT 
      i.InspectorID,
      COUNT(v.visitID)
   FROM 
      dbo.Inspectors i
   INNER JOIN
      dbo.InspectionScope s ON s.InspectorId = i.InspectorId
   INNER JOIN
      dbo.Visits v ON s.ScopeID = v.ScopeID 
   INNER JOIN
      dbo.VisitDocs vd ON vd.VisitID = v.VisitID 
   WHERE 
      vd.DocType IN (1, 2, 13) 
      AND v.VisitDate BETWEEN @DateFrom AND @DateTo
   GROUP BY
      s.InspectorID 

SELECT * FROM @Report

【讨论】:

  • @user1263981:那么您需要开始检查您的索引 - 所有的 foreign key 列都正确索引了吗?你有vd.DocTypev.VisitDate 的索引吗??
【解决方案3】:

不再推荐使用光标。最好将数据插入临时表并为其添加主键。

因此,在您的循环中,您将有一个 while 循环,该循环使用临时表中您的 Id 上的 WHERE 子句循环遍历您的表。

这要快得多。

【讨论】:

    猜你喜欢
    • 2020-11-06
    • 2022-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多