优化SQL Server查询(CTE)

时间:2016-09-12 16:49:41

标签: sql sql-server query-optimization

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

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

2 个答案:

答案 0 :(得分:0)

您不需要CTE:

gap = [list(map(list, zip([[y] for y in years], item))) for item in temp]

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

答案 1 :(得分:0)

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

(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的小时数并从那里决定。 =&GT; 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上的索引。理想情况下,netlockid字段上具有聚簇索引(或PK),在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子句中进行这种转换,它将混淆优化器,结果可能是毁灭性的你的查询的表现)