繁体   English   中英

优化SQL Server查询(CTE)

[英]Optimizing SQL Server Query (CTE)

此查询需要永远运行。 有没有人对如何优化它有任何好的建议?

WITH CTE (Lockindate, Before5, After5) AS (SELECT nl.Lockindate, 
(CASE WHEN CAST(RIGHT(FirstLockActivity,8) AS time(1)) <= '17:00' THEN
'Before 5 PM' END) AS before5,
(CASE WHEN CAST(RIGHT(FirstLockActivity,8) AS time(1)) >= '17:00' THEN
'After 5 PM' END) AS after5
FROM netlock nl WITH(NOLOCK)
JOIN rate rs WITH(NOLOCK)
ON nl.id=rs.id
WHERE nl.lockindate BETWEEN '2016-08-01' AND '2016-08-31')
SELECT lockindate, COUNT(After5), COUNT(Before5)
FROM CTE
GROUP BY lockindate

您不需要CTE:

SELECT nl.Lockindate, 
   SUM(CASE WHEN CAST(RIGHT(FirstLockActivity,8) AS time(1)) <= '17:00' THEN 1 ELSE 0 END) AS before5,
   SUM(CASE WHEN CAST(RIGHT(FirstLockActivity,8) AS time(1)) > '17:00' THEN 1 ELSE 0 END) AS after5
FROM netlock nl WITH(NOLOCK)
JOIN rate rs WITH(NOLOCK)
ON nl.id=rs.id
WHERE nl.lockindate BETWEEN '2016-08-01' AND '2016-08-31'
GROUP BY nk.lockindate

但这可能导致完全相同的计划。 然后你需要检查它为什么慢(连接/组上的索引缺少列?)

虽然在这种情况下 (*)它不会加速所有事情,但是从一种数据类型到另一种数据类型的转换然后再次转换为“不是最佳的”。

(CASE WHEN CAST(RIGHT(FirstLockActivity,8) AS time(1)) <= '17:00' THEN 'Before 5 PM' END) AS before5,

首先,您执行从datetime [FirstLockActivity]到字符串的隐式转换。 这是因为Right()函数需要一个字符串,因此需要转换。 进行隐式转换可能很危险。 根据服务器的配置(甚至连接本身可能受操作系统区域设置的影响),这可能会产生令人惊讶的结果,并非所有结果都具有预期的最后8个字符!

仅供参考:请看这里: https ://msdn.microsoft.com/en-us/library/ms187928.aspx?f = 255&MSPPError = -2147217396 由于你无法用CAST显式传递'style',我总是建议人们在从datetimes转换为string时使用Convert(),ESPECIALLY,反之亦然。

之后,您取最右边的8个字符,然后将它们转换为时间(1)。 我不确定你为什么要在一段时间(0)内使用一个时间(1),因为你只对小时部分感兴趣,但最后它不会有太大的区别我猜。

无论如何,做所有这些转换需要CPU,因此需要时间。

假设这个事情并没有从你真正想做的事情中黯然失色,那么查询的目标是返回每个lockindate在下午5点之前和之后有多少条目的指示。 因此,您需要做的就是计算每个FirstLockActivy的小时数并从那里决定。 => Hour(xx)DatePart(hour, xx)都将返回所需信息,而这些信息只占这些转换的CPU成本的一小部分。 此外,您可以轻松地在一个CASE结构中获得前/后。

  WITH CTE (LockinDate, Before5PM)
    AS (SELECT Lockindate, 
               (CASE WHEN Hour(FirstLockActivity) < 17 THEN 1 ELSE 0 END) AS Before5PM

          FROM netlock nl WITH (NOLOCK)
          JOIN rate rs WITH (NOLOCK)
            ON nl.id=rs.id
         WHERE nl.lockindate BETWEEN Convert(datetime, '2016-08-01', 105) AND Convert(datetime, '2016-08-31', 105))
  SELECT LockinDate,
         After5  = SUM(1 - Before5PM),
         Before5 = SUM(Before5PM)  
    FROM CTE
   GROUP BY LockinDate

假设表中(相关)记录的数量很大,那么这将对持续时间产生一些影响,但考虑到现代处理器的速度,它可能不会令人震惊。 当然,当你在繁忙的服务器上并没有那么多(免费)CPU到处时,那么效果会更加明显。

也就是说,至于性能,我建议检查netlockrate table上的索引。 理想情况下netlock对聚簇索引(或PK) id字段和在非聚集索引lockindate 此外, rate表在id字段上有一个聚集索引,还有一些我不知道的其他字段。 如果后者不是这种情况,那么在id字段上使用包含列中的FirstLockActivity字段的非聚集索引也会很棒。

如果您希望在没有CTE的情况下进行此查询,则只需将CTE复制粘贴到子查询/派生表中,如下所示:

 SELECT LockinDate,
        After5  = SUM(1 - Before5PM),
        Before5 = SUM(Before5PM)  
   FROM (SELECT Lockindate, 
                (CASE WHEN Hour(FirstLockActivity) < 17 THEN 1 ELSE 0 END) AS Before5PM

           FROM netlock nl WITH (NOLOCK)
           JOIN rate rs WITH (NOLOCK)
             ON nl.id=rs.id
          WHERE nl.lockindate BETWEEN Convert(datetime, '2016-08-01', 105) AND Convert(datetime, '2016-08-31', 105)) A
  GROUP BY LockinDate

或者,你得到的更多

 SELECT LockinDate,
        After5  = SUM(1 - (CASE WHEN Hour(FirstLockActivity) < 17 THEN 1 ELSE 0 END)),
        Before5 = SUM(    (CASE WHEN Hour(FirstLockActivity) < 17 THEN 1 ELSE 0 END))
   FROM netlock nl WITH (NOLOCK)
   JOIN rate rs WITH (NOLOCK)
     ON nl.id=rs.id
  WHERE nl.lockindate BETWEEN Convert(datetime, '2016-08-01', 105) AND Convert(datetime, '2016-08-31', 105)) A
  GROUP BY LockinDate

PS:在浏览器中写下所有这些,可能会有一些拼写错误,并且没有代码经过测试,准备不得不摆弄一下以使其工作=)

PS:如果您无法获得查询计划,您仍然可以使用SET STATISTICS TIME来更轻松地将查询的一个版本与另一个版本进行比较。

(*:如果你在WHERE子句或JOIN子句中进行这种转换,它会混淆优化器,结果可能对查询的性能造成破坏)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM