【问题标题】:How can I optimize my SQL query to run faster?如何优化我的 SQL 查询以更快地运行?
【发布时间】:2014-09-20 00:46:43
【问题描述】:
SELECT 
   b.User_Id
   ,(CONVERT(varchar, DATEADD(hh, - 7, b.callstartdt), 101))as 'Dt'
   ,(COUNT(distinct b.SeqNum ) + Count(distinct c.SeqNum) + count(distinct d.seqnum)) as 'TotalCalls'
   ,COUNT(distinct b.SeqNum )as 'ACD'
   ,COUNT(distinct c.SeqNum)as 'AOD'
   ,COUNT(distinct d.seqnum)  as 'Manual'
   ,COUNT(distinct e.SeqNum)as 'Contacts'
   ,COUNT (distinct es.seqnum) as 'Success'
FROM 
   [detail_epro].[dbo].[ACDCallDetail]as b 
LEFT JOIN 
   [detail_epro].[dbo].[AODCallDetail]as c on c.User_Id = b.User_Id   
LEFT JOIN
   [detail_epro].[dbo].[manualCallDetail]as d on d.User_Id = b.User_Id 
LEFT JOIN
   (SELECT 
       USER_ID, CallStartDt, SeqNum
    FROM 
       [detail_epro].[dbo].[AgentDispoDetail]
    WHERE 
        Disp_Id IN 
                (100000150, 100000126,  100000137,  100000093,  100000133,  
                100000123,  100000094,  100000161,  100000162,  100000085,  
                100000084,  100000086,  100000096,  100000087,  100000157,  
                100000088,  100000097,  100000154,  100000148,  100000134,  
                100000131,  100000160,  100000156,  100000165,  100000166,  
                100000122,  100000121,  100000138,  100000130,  100000144,  
                100000132,  100000158,  100000098,  100000147,  100000100,  
                100000153,  100000139,  100000145,  100000101,  100000140,  
                100000102,  100000103,  100000104,  100000105,  100000106,  
                100000159,  100000112,  100000135,  100000090,  100000113,  
                100000141,  100000146,  100000115,  100000108,  100000092,  
                100000155,  100000125,  100000151,  100000136,  100000107,  
                100000142) 
   ) AS e ON e.User_Id = b.User_Id
LEFT JOIN 
   (SELECT 
        USER_ID, CallStartDt, SeqNum
    FROM 
        [detail_epro].[dbo].[AgentDispoDetail]
    WHERE Disp_Id IN 
                (100000150, 100000137,  100000093,  100000133,  100000123,  
                100000094,  100000161,  100000085,  100000086,  100000157,  
                100000088,  100000131,  100000160,  100000156,  100000165,  
                100000166,  100000122,  100000121,  100000138,  100000144,  
                100000132,  100000098,  100000100,  100000153,  100000139,  
                100000145,  100000101,  100000140,  100000102,  100000103,  
                100000105,  100000106,  100000159,  100000112,  100000135,  
                100000141,  100000146,  100000115,  100000108,  100000092,  
                100000155,  100000125,  100000151,  100000136,  100000107) 
   ) AS es ON es.User_Id = b.User_Id
WHERE
   (CONVERT(varchar, DATEADD(hh, - 7, b.CallStartDt), 101)) = (CONVERT(varchar, DATEADD(hh, - 7, c.CallStartDt), 101))
   AND (CONVERT(varchar, DATEADD(hh, - 7, b.CallStartDt), 101))= (CONVERT(varchar, DATEADD(hh, - 7, d.CallStartDt), 101))
   AND (CONVERT(varchar, DATEADD(hh, - 7, b.CallStartDt), 101))= (CONVERT(varchar, DATEADD(hh, - 7, e.CallStartDt), 101)) 
   AND (CONVERT(varchar, DATEADD(hh, - 7, b.CallStartDt), 101))= (CONVERT(varchar, DATEADD(hh, - 7, es.CallStartDt), 101))
   AND (CONVERT(varchar, DATEADD(hh, - 7, b.CallStartDt), 101)) >= '08/01/2014'
GROUP BY
   b.User_Id, b.CallStartDt

运行此查询花费的时间比我想运行的要长,超过一分钟,我猜这与服务器有很大关系,但我想我会问是否有人有任何想法让这个更快

查询是获取一些电话代理数据,没有任何表格汇总

  • acd = 呼入电话
  • aod = 拨号器调用
  • manual = 手动调用
  • 联系人 = 基于代理将使用的处置代码
  • 成功 = 是联系人的子集

【问题讨论】:

  • 您到底为什么要将日期时间转换为VARCHAR,甚至更糟的是转换为'08/01/2014' 格式?
  • 在这里,查看接受的答案以了解解释是什么stackoverflow.com/a/7359705/1745672
  • 如果必须这样设置,那么我会考虑使用 DatePart 而不是投射整个日期。
  • 要清楚,当您投射日期并比较它们时会发生什么,sql 必须逐个字符地检查每个日期并比较每个单独的字符。您的每个日期比较现在都在进行十次比较,而不是一次。
  • 除了其他人投入到环中的内容之外,您还有 LEFT-JOIN,但随后在 WHERE 子句中明确测试各自“c”、“d”、“e”和“ es" 别名,因此将其转换为所有 INNER JOIN。这是你的意图吗?

标签: sql sql-server optimization lag


【解决方案1】:

查询存在许多问题,但我注意到的第一件事是日期时间转换效率低下。所以,在检查索引和执行计划之前,我会先从那部分开始。

我想您想检查各种日期时间是否在同一日期(减去 7 小时,这可能是您的时区,而数据以 UTC 存储时)。所以,让我们试试这个,而不是那个(可怕的)WHERE

CROSS APPLY
    ( SELECT dt = DATEADD(hour, -7, b.CallStartDt) ) AS x
CROSS APPLY
    ( SELECT dt = DATEADD(day, +1, x.dt) ) AS y
WHERE
       b.CallStartDt >= DATEADD(hour, +7, '20140801')
  AND  c.CallStartDt >= x.dt AND  c.CallStartDt < y.dt
  AND  d.CallStartDt >= x.dt AND  d.CallStartDt < y.dt
  AND  e.CallStartDt >= x.dt AND  e.CallStartDt < y.dt
  AND es.CallStartDt >= x.dt AND es.CallStartDt < y.dt

解释/注释:

  • (CONVERT(varchar, DATEADD(hh, - 7, b.CallStartDt), 101)) &gt;= '08/01/2014' 完全错误。它不仅使用低效的转换,还会返回错误的结果。因为日期(作为字符串和日期)'08/03/2014''08/01/2014' 之后,但对于其他示例,情况正好相反:'09/09/2011' &gt; '08/01/2014' 但显然 2011 年在 2014 年之前。

  • 所有对DATEDIFF()CONVERT() 的不必要调用均已删除。这样,不仅不会完成对函数的数千次调用(或数百万次调用,具体取决于您的表大小),而且如果这些列上有索引,优化器将能够在各种条件下使用索引。

  • 只保留了b.CallStartDt 的(-7 小时),因为如果不更改表就无法避免这种情况(尽管添加带有索引的计算列会有所帮助。)

  • 对日期和日期时间使用合理的格式,例如 '20140801' ('YYYYMMDD'),正如 Aaron Bertrand 的博客所解释的,这是在 SQL-Server 中使用日期的唯一 100% 安全格式。请参阅:Bad habits to kick : mis-handling date / range queries

  • 使用DATEADD() 函数,使用长格式。 hour 而不是hhday 而不是ddminute 而不是mm(或者是mi?还是min?,我一直忘记。) .

要做的更多:

  • 上述 4 个条件(关于 cdees 表的条件)可能应该移动到各自的 LEFT 连接(正如 DRapp 评论的那样。)

  • 检查执行计划以及索引是否可用和使用。

小细节:

【讨论】:

  • +1 - 假设他在 2005 年或更晚,但比转换好得多
  • @第十二。谢谢。我猜你说的是CROSS APPLY。这可以改变,但表达式必须写四次。不是效率问题,只是代码少一点。
  • @ypercube 哇! ...我感谢所有的反馈...每天都在学习!我感谢所有关于最佳实践的反馈和阅读!
【解决方案2】:

你需要删除你的子查询并放

WHERE Disp_Id IN 
(100000150, 100000137,  100000093,  100000133,  100000123,  
100000094,  100000161,  100000085,  100000086,  100000157,  
100000088,  100000131,  100000160,  100000156,  100000165,  
100000166,  100000122,  100000121,  100000138,  100000144,  
100000132,  100000098,  100000100,  100000153,  100000139,  
100000145,  100000101,  100000140,  100000102,  100000103,  
100000105,  100000106,  100000159,  100000112,  100000135,  
100000141,  100000146,  100000115,  100000108,  100000092,  
100000155,  100000125,  100000151,  100000136,  100000107)

在临时表中并将该临时表连接到 Disp_ID 上的 [detail_epro].[dbo].[AgentDispoDetail]。

正如其他人已经发布的那样,您为什么要使用转换来比较日期?它们在每张桌子上的格式不同吗?如果他们不删除转换者。

另外,删除 DATEADDS。您在比较中使用它们,但在等式的每一半中添加 -7。就是这样:X + 7 = 8 + 7。去掉7,x的值没有变化。

【讨论】:

  • 我解释了我认为上面的日期会发生什么(我猜它们存储为日期时间,这意味着它们永远不会完全匹配)临时表 +1,并且还去掉了比较两边的 -7。
【解决方案3】:

作为一个更新,因为我没有发布答案,我最终将很多查询分成带有临时表的较小查询,这样做加快了查询和处理 1000%

最终转换为CTE并保存为视图

谢谢大家

【讨论】:

    【解决方案4】:

    不确定您的语言环境,但您可能需要考虑更好的 UTC 转换

        dateadd(minute, datediff(minute, sysutcdatetime(), sysdatetime()), b.callstartdt)
    

    这也考虑了夏令时,这又取决于您的语言环境。

    【讨论】:

      猜你喜欢
      • 2016-02-27
      • 2012-06-27
      • 1970-01-01
      • 2015-05-22
      • 2017-11-15
      • 1970-01-01
      • 2011-06-04
      • 2021-11-11
      • 2016-03-18
      相关资源
      最近更新 更多