SQL查询耗时太长,有没有可能的优化,需要指向正确的方向

时间:2014-01-27 15:16:01

标签: sql sql-server sql-server-2008 sql-server-2005

问题

我最近了解了CTE以及如何使用它们来获得性能,但我认为在这种情况下我过度使用它们。以下查询大约需要1分钟,只有1000次考试,我相信它可以更快地完成。请建议可以指向正确方向的优化或术语?

上下文

要理解查询,您需要有一些上下文,所以在这里。在数据库中,我们有考试,这将在对象上执行(E.G.如果笔可以写,则需要检查笔)。在考试开始前,必须确认考试,因此确认日期。当然,我们也会在创建考试时保存( createdate )。当考试准备就绪时(准备日期),它需要得到一个结果。结果可以被批准(已批准)或被拒绝(被拒绝)。此查询的含义是创建图表,显示每天的5个检查条件。

每天所需的计数:

  1. examinations_created_count
  2. examinations_acknowledged_count
  3. examinations_ready_count
  4. examinations_ready_and_approved_count
  5. examinations_ready_and_rejected_count
  6. 实际查询

    DECLARE @ProjectID [bigint] = 3
    DECLARE @LocationID [bigint] = 2
    DECLARE @PersonID [bigint] = 1058
    DECLARE @StartDate [datetime] = '2012-01-01' 
    DECLARE @EndDate [datetime]= '2014-12-31'
    
    ;WITH ExaminationCTE(acknowledgedate, readydate, createdate, isrejected, isapproved) 
    AS
    (
        SELECT [acknowledgedate], [readydate], createdate, [isrejected], [isapproved]  FROM [dbo].[Examination]
        LEFT JOIN [dbo].[Object] [o] ON [dbo].[Examination].[fk_objectid] = [o].[pk_objectid]
        LEFT JOIN [dbo].[ExaminationResults] [er]  ON [dbo].[Examination].[fk_examinationid] = [er].[pk_examinationid]
        WHERE [fk_projectid] = @ProjectID 
        AND [fk_locationid] = @LocationID 
        AND [fk_personid] = @PersonID
    ),
    dates(Date) AS 
    (
        --Get all the dates between the selected range
        SELECT @StartDate as Date
        UNION ALL
        SELECT DATEADD(d,1,[Date])
        FROM dates 
        WHERE DATE < @EndDate
    )
    
    -- Select all the counts from the examination CTE
    SELECT d.Date,
           (SELECT COUNT(*) FROM [ExaminationCTE] WHERE [createdate] <= [d].[Date]) AS 'examinations_created_count',
           (SELECT COUNT(*) FROM [ExaminationCTE] WHERE [acknowledgedate] <= [d].[Date]) AS 'examinations_acknowledged_count',
           (SELECT COUNT(*) FROM [ExaminationCTE] WHERE [readydate] <= [d].[Date]) AS 'examinations_ready_count',
           (SELECT COUNT(*) FROM [ExaminationCTE] WHERE [readydate] <= [d].[Date] AND [isapproved] = 1) AS 'examinations_ready_and_approved_count',
           (SELECT COUNT(*) FROM [ExaminationCTE] WHERE [readydate] <= [d].[Date] AND [isrejected] = 1) AS 'examinations_ready_and_rejected_count'      
    FROM dates d
    OPTION (MAXRECURSION 0)
    

    当查询正在运行时,它不符合我的性能规格。我觉得这可以做得更整洁。

    更新 由于@GordonLinoff的帮助,我几乎就在那里。但是我需要用最后一个可能的值填充NULL行。

    This image shows what the null values must be

    在图像中,您可以看到结果为空箭头,指出值应该是什么,对于第一组NULL我需要从所选开始日期之前的最后一个可用日期获取值。

1 个答案:

答案 0 :(得分:1)

是的,你可以提高效率。你正在做一大堆相关的非等值连接,然后计算结果。

采取不同的方法。一种简单的方法是按日期汇总ExaminationCTE,以总结结果。但是,在SQL Server中,CTE未实现,因此这可能不会改变性能。

另一种方法是使用窗口函数进行计数。以下是第一个字段的示例:

from dates left outer join
     (select createdate, max(seqnum) as examinations_created_count
      from (select createdate, row_number() over (order by createdate) as seqnum
            from ExaminationCTE
           ) e
      group by createdate
     ) ecc
     on dates.date = ecc.createdate 

其他人会类似。实际上,您可以将所有变量放在一个子查询中。

逻辑是为每一行添加一个序号,然后取每个日期的最大数量。这是计数。