在CTE中使用局部变量会导致急剧减速

时间:2018-03-27 16:50:06

标签: sql-server tsql common-table-expression

当我在CTE之前声明一个变量时,它需要大约5倍才能完成。我在SQL Server 2008 R2上。

我的数据库中的时间存储为Unix时间,因此我将一些变量从本地时间转换为Unix时间。现在我只包括Unix部分。积累是生产的总和,也是我使用CTE的原因。代码的以下部分大约需要5分钟才能执行。

Declare @StartTime bigint
Declare @EndTime bigint
declare @ticksPerDay bigint

Set @StartTime = 635330772000000000
set @EndTime = 635357556000000000
set @ticksPerDay = 864000000000

WITH MyCTE as

    (SELECT ROW_NUMBER() OVER (ORDER BY CH.countercode, CH.Time ASC) AS 
    Sequence, CH.PlantCode, CH.CounterCode, CH.Time, CH.Accumulation
        ,DateAdd(mi, DATEDIFF(mi,getutcdate(), GetDate()),
        DateAdd(d,Cast((CH.Time * Power(10.00000000000,-7)/60/60/24) as 
        int), CAST('0001-01-01' as Date)) + Cast(ch.Time * 
        Power(10.00000000000,-7)/60/60/24%1 as Datetime)) as LocalTime

    FROM eit.CounterHist CH

        INNER JOIN eit.CounterBasic cb on ch.CounterCode = cb.CounterCode
        INNER JOIN eit.LineEquipment le on  cb.PlantElementCode = 
        le.PlantElementCode

    Where CH.Time>=@StartTime -(@ticksPerDay/24) and CH.Time<=@EndTime and 
        le.IsCriticalMachine = 1)

SELECT c1.PlantCode, c1.CounterCode, c1.time, c1.LocalTime, 
COALESCE(c1.Accumulation - c2.Accumulation, 0) AS Prod

FROM MyCTE AS c1
    Inner JOIN MyCTE AS c2 ON c1.Sequence = c2.Sequence + 1

Where c1.Time>=@StartTime and c1.Time<=@EndTime
order by c1.Sequence ASC;

另一方面,如果我只是将时间硬编码到where子句而不是使用变量,则运行大约需要1分钟。代码看起来如下(请查看where子句):

WITH MyCTE as

   (SELECT ROW_NUMBER() OVER (ORDER BY CH.countercode, CH.Time ASC) AS 
   Sequence, CH.PlantCode, CH.CounterCode, CH.Time, CH.Accumulation,

    DateAdd(mi, DATEDIFF(mi,getutcdate(), GetDate()),
    DateAdd(d,Cast((CH.Time * Power(10.00000000000,-7)/60/60/24) as int), 
    CAST('0001-01-01' as Date)) + Cast(ch.Time * 
    Power(10.00000000000,-7)/60/60/24%1 as Datetime)) as LocalTime

   FROM eit.CounterHist CH
     INNER JOIN eit.CounterBasic cb on ch.CounterCode = cb.CounterCode
     INNER JOIN eit.LineEquipment le on  cb.PlantElementCode = 
     le.PlantElementCode

   Where CH.Time>=635330772000000000 -(864000000000/24) and 
    CH.Time<=635357556000000000 and le.IsCriticalMachine = 1)

SELECT c1.PlantCode, c1.CounterCode, c1.time, c1.LocalTime, 
 COALESCE(c1.Accumulation - c2.Accumulation, 0) AS BottleCount

FROM MyCTE AS c1
  Inner JOIN MyCTE AS c2 ON c1.Sequence = c2.Sequence + 1

Where c1.Time>=635330772000000000 and c1.Time<=635357556000000000
order by c1.Sequence ASC

有没有办法在CTE中使用局部变量或其他东西,因为在Unix时间输入很困难。

编辑:正面加载更多的代码(电源部分)并没有真正节省任何时间。我看到的大部分时间都在Where子句的CTE部分。

Where CH.Time>=@StartTime
Where CH.Time>=635330772000000000

当我使用@StartTime时,它需要的时间比第二个选项中的硬编码要长5倍。

我真的想避免硬编码,因为我希望能够输入正常日期,例如'2018-03-14 06:00:00'并转换为上述时间格式。

2 个答案:

答案 0 :(得分:1)

查询优化器可以预先使用硬编码值做更多事情。

预先计算更多内容:

Declare @StartTime bigint   = 635330772000000000
Declare @EndTime bigint     = 635357556000000000
declare @ticksPerDay bigint = 864000000000 
declare @StartTimeAdj bigint = @StartTime - (ticksPerDay / 24) 
declare @pwr float = Power(10.00000000000,-7)/60/60/24

WITH MyCTE as
(SELECT ROW_NUMBER() OVER (ORDER BY CH.countercode, CH.Time ASC) AS Sequence
      , CH.PlantCode, CH.CounterCode, CH.Time, CH.Accumulation
      , DateAdd(mi, DATEDIFF(mi,getutcdate(), GetDate()), DateAdd(d,Cast((CH.Time * @pwr) as int), CAST('0001-01-01' as Date)) + Cast(ch.Time * @pwr%1 as Datetime) 
               ) as LocalTime
 FROM eit.CounterHist CH
     INNER JOIN eit.CounterBasic cb on cb.CounterCode = ch.CounterCode  
     INNER JOIN eit.LineEquipment le on le.PlantElementCode = cb.PlantElementCode
 Where CH.Time >= @StartTimeAdj
   and CH.Time <= @EndTime  
   and le.IsCriticalMachine = 1
)

SELECT c1.PlantCode, c1.CounterCode, c1.time, c1.LocalTime, 
       COALESCE(c1.Accumulation - c2.Accumulation, 0) AS Prod    
FROM MyCTE AS c1
    Inner JOIN MyCTE AS c2 ON c1.Sequence = c2.Sequence + 1    
Where c1.Time> = @StartTime and c1.Time <= @EndTime
order by c1.Sequence ASC;  

我非常怀疑

Inner JOIN MyCTE AS c2 ON c1.Sequence = c2.Sequence + 1 

你可以用铅或滞后来削减工作量半就

WITH MyCTE as
(SELECT lead(Accumulation) OVER (ORDER BY CH.countercode, CH.Time ASC) AS LeadAccumulation
      , CH.PlantCode, CH.CounterCode, CH.Time, CH.Accumulation
      , DateAdd(mi, DATEDIFF(mi,getutcdate(), GetDate()), DateAdd(d,Cast((CH.Time * @pwr) as int), CAST('0001-01-01' as Date)) + Cast(ch.Time * @pwr%1 as Datetime) 
               ) as LocalTime
 FROM eit.CounterHist CH
     INNER JOIN eit.CounterBasic cb on cb.CounterCode = ch.CounterCode  
     INNER JOIN eit.LineEquipment le on le.PlantElementCode = cb.PlantElementCode
 Where CH.Time >= @StartTimeAdj
   and CH.Time <= @EndTime  
   and le.IsCriticalMachine = 1
)

SELECT c1.PlantCode, c1.CounterCode, c1.time, c1.LocalTime, 
       COALESCE(c1.Accumulation - c1.LeadAccumulation, 0) AS Prod    
FROM MyCTE AS c1
Where c1.Time> = @StartTime and c1.Time <= @EndTime
order by c1.countercode, c1.Time ASC;

答案 1 :(得分:0)

当您将硬编码变量放入递归CTE时,数据结果会生成一次或进行优化,但如果您输入变量,CTE会重新生成多次。

考虑创建CTE语句的临时表。不要忘记在最后删除临时表。