CTE self join 减慢执行速度

CTE self join slow down the execution

我在 SP 中使用以下查询。

DECLARE @DateFrom  datetime = '01/01/1753',       
     @DateTo   datetime = '12/31/9999'      
     BEGIN 
     WITH tmpTethers      
       AS      
       (      
        SELECT TL.str_systemid               AS SystemCode,       
          ISNULL(ml.name, ml.location)  AS [System],      
          TL.dte_created                AS [Date],       
          TL.str_LengthId               AS TetherRegId,       
          0                             AS LengthCut,      
          ISNULL(TL.dbl_newlength, 0)   AS LengthAdded,       
          CAST(0 AS FLOAT)     AS RemainingLength,      
          1 AS Mode,      
          UT.description      AS UOM      
        FROM OP_TetherLength AS TL      
          INNER JOIN master_location  AS ML  ON ML.location = TL.str_systemid      
          LEFT JOIN udc_type AS UT ON TL.lng_lengthuom = UT.udc      
        WHERE (TL.dte_dateadded BETWEEN  @DateFrom AND @DateTo)  


          
    UNION ALL      
          
    SELECT RR.systemcode     AS SystemCode,       
      ISNULL(ML.name, ML.location) AS [System],      
      RR.datecreated               AS [Date],       
      RR.oms_repairid              AS TetherRegId,       
      ISNULL(RR.cutlength, 0)      AS LengthCut,      
      0                            AS LengthAdded,       
      0              AS RemainingLength,      
      0 AS Mode,      
      UT.description      AS UOM      
    FROM Repair_Registration AS RR        
      INNER JOIN master_location AS ML ON RR.systemcode =  ml.location      
      LEFT JOIN udc_type AS UT ON RR.cutlength_uomid = UT.udc      
    WHERE --RR.cut_umbilical_tether = 0 AND      
  RR.cutbackrequired = 1 AND      
      (RR.datecreated BETWEEN  @DateFrom AND @DateTo)            
   ),      
         
   tmpOrderedTethers      
   AS      
   (      
    SELECT TOP 1000      
      SystemCode,       
      [System],      
      [Date],       
      TetherRegId,       
      LengthCut,      
      LengthAdded,       
      RemainingLength,      
      Mode,      
      UOM,      
      ROW_NUMBER() OVER(PARTITION BY SystemCode  ORDER BY [Date] ) AS RowNumber      
    FROM tmpTethers      
    ORDER BY SystemCode      
   ),      
         
   tmpFinalTethers      
   AS      
   (        
    SELECT SystemCode,       
      [System],      
      [Date],       
      TetherRegId,       
      LengthCut,      
      LengthAdded,       
      CASE      
       WHEN Mode = 1 THEN LengthAdded      
       ELSE 0 - LengthCut      
      END AS RemainingLength,      
      Mode,      
      UOM,      
      RowNumber      
    FROM tmpOrderedTethers      
    WHERE RowNumber = 1      
  
    UNION ALL      
      
    SELECT tmpOT.SystemCode,       
      tmpOT.[System],      
      tmpOT.[Date],       
      tmpOT.TetherRegId,       
      tmpOT.LengthCut,      
      tmpOT.LengthAdded,       
      CASE      
       WHEN tmpOT.Mode = 1 THEN /*tmpFT.RemainingLength +*/ tmpOT.LengthAdded      
       ELSE tmpFT.RemainingLength - tmpOT.LengthCut      
      END AS RemainingLength,      
      CASE      
       WHEN tmpOT.Mode = 1 OR tmpFT.Mode = 1 THEN 1      
       ELSE 0      
      END AS Mode,      
      tmpOT.UOM,      
      tmpOT.RowNumber      
    FROM tmpOrderedTethers AS tmpOT      
    INNER JOIN tmpFinalTethers AS tmpFT  ON tmpFT.SystemCode = tmpOT.SystemCode AND      
                tmpFT.RowNumber = tmpOT.RowNumber - 1      
   ),

  
 
   ---- FT - Previous      
   ---- OT - Current      
    
   SELECT SystemCode,       
     [System],      
     [Date],       
     TetherRegId,       
     LengthCut,      
     LengthAdded,       
     RemainingLength,      
     UOM,      
     RowNumber     
     ,ROW_NUMBER() OVER(PARTITION BY SystemCode  ORDER BY [Date] desc) AS SortNumber
   FROM tmpGetFinalTethers      
   ORDER BY SystemCode, SortNumber      
   OPTION (MAXRECURSION 1000)
END

在上面的查询中,当我评论以下部分时,执行时间减少并且数据来得快:

SELECT tmpOT.SystemCode,       
          tmpOT.[System],      
          tmpOT.[Date],       
          tmpOT.TetherRegId,       
          tmpOT.LengthCut,      
          tmpOT.LengthAdded,       
          CASE      
           WHEN tmpOT.Mode = 1 THEN /*tmpFT.RemainingLength +*/ tmpOT.LengthAdded      
           ELSE tmpFT.RemainingLength - tmpOT.LengthCut      
          END AS RemainingLength,      
          CASE      
           WHEN tmpOT.Mode = 1 OR tmpFT.Mode = 1 THEN 1      
           ELSE 0      
          END AS Mode,      
          tmpOT.UOM,      
          tmpOT.RowNumber      
        FROM tmpOrderedTethers AS tmpOT      
        INNER JOIN tmpFinalTethers AS tmpFT  ON tmpFT.SystemCode = tmpOT.SystemCode AND      
                    tmpFT.RowNumber = tmpOT.RowNumber - 1 

请告诉我如何改进它。

您的 [tmpFinalTethers][tmpGetFinalTethers] cte 中似乎有逐行处理。

[tmpFinalTethers] 中返回的每一行都基于 [tmpOrderedTethers],而 [tmpOrderedTethers] 的数据基于 [tmpTethers]。因此 [tmpOrderedTethers][tmpTethers] 中包含的逻辑将被执行 n 次,其中 n 是 [tmpFinalTethers].

返回的行数

原因是因为cte不是物化对象。它们不会存储在内存或光盘中,因此每次您在声明之外引用它们时它们都会执行。

如果您确实需要逐行处理您的任务并且没有其他选择,将 [tmpOrderedTethers] 的结果集加载到临时 table 可能会有所帮助。

而且你的[tmpFinalTethers][tmpGetFinalTethers]好像里面的逻辑是一样的。我不确定它的目的是什么。 Mb 你可以从 [tmpFinalTethers] 做最后的 select 并摆脱 [tmpGetFinalTethers].

已编辑:

像这样试试:

;WITH tmpTethers AS (...),
tmpOrderedTethers AS (...)
SELECT * INTO #tmpOrderedTethers FROM tmpOrderedTethers

;WITH tmpFinalTethers (
SELECT ... FROM #tmpOrderedTethers WHERE ...

UNION ALL

SELECT ... FROM #tmpOrderedTethers tmpOT INNER JOIN ...
)

已编辑 2:

因为你有 OPTION (MAXRECURSION 1000) 我想你总是得到 1000<= 行数。对于如此多的行,您使用递归 cte 结合 temp table 的解决方案可能会起作用。至少会比cursor好,因为它除了逐行处理外,还要消耗一些资源。但是如果你需要处理比方说 10 000 行,那么逐行处理绝对不是合适的解决方案,你应该找到另一个。