【问题标题】:Performance issue with CTE SQL Server queryCTE SQL Server 查询的性能问题
【发布时间】:2016-07-04 21:59:44
【问题描述】:

我们有一个具有父子关系的表,它表示一个深层的树结构。

我们正在使用带有 CTE 的视图来查询数据,但性能很差(请参阅下面的代码和执行计划)。

有什么方法可以提高性能吗?

WITH cte (ParentJobTypeId, Id) AS 
( 
   SELECT   
       Id, Id 
   FROM     
       dbo.JobTypes 

   UNION ALL 

   SELECT   
       e.Id, cte.Id 
   FROM     
       cte 
   INNER JOIN 
       dbo.JobTypes AS e ON e.ParentJobTypeId = cte.ParentJobTypeId 
) 
SELECT  
    ISNULL(Id, 0) AS ParentJobTypeId,
    ISNULL(ParentJobTypeId, 0) AS Id
FROM    
    cte

【问题讨论】:

  • 我的意思是,有多少层?
  • 700k 点的层次结构?它多久改变一次?
  • 完整的层次是否需要是动态的,或者是否可以根据需要存储和/或重建。
  • 我有我认为的 127K 的大层次结构。你赢了。如果可以存储。考虑添加范围键以加速导航和聚合
  • 一秒钟我会发布一个快速示例

标签: sql-server tsql


【解决方案1】:

使用范围键的简单示例。正如我之前提到的,层次结构是 127K 点,有些部分有 15 级深度

cte 构建,假设其结果将存储在一个表中(也有索引)

Declare @Table table(ID int,ParentID int,[Status] varchar(50))
Insert @Table values
(1,101,'Pending'),
(2,101,'Complete'),
(3,101,'Complete'),
(4,102,'Complete'),
(101,null,null),
(102,null,null)


;With cteOH (ID,ParentID,Lvl,Seq) 
 as (
     Select ID,ParentID,Lvl=1,cast(Format(ID,'000000') + '/' as varchar(500)) from @Table where ParentID is null 
     Union All
     Select h.ID,h.ParentID,cteOH.Lvl+1,Seq=cast(cteOH.Seq + Format(h.ID,'000000') + '/' as varchar(500)) From @Table h INNER JOIN cteOH ON  h.ParentID = cteOH.ID
    ),
    cteR1 as (Select ID,Seq,R1=Row_Number() over (Order by Seq) From cteOH),
    cteR2 as (Select A.ID,R2 = max(B.R1) From cteOH A Join cteR1 B on (B.Seq Like A.Seq+'%') Group By A.ID)
    Select B.R1
          ,C.R2
          ,A.Lvl
          ,A.ID
          ,A.ParentID 
    Into  #TempHier
    From  cteOH A 
    Join  cteR1 B on (A.ID=B.ID) 
    Join  cteR2 C on (A.ID=C.ID) 

    Select * from #TempHier

    Select H.R1
          ,H.R2
          ,H.Lvl
          ,H.ID
          ,H.ParentID
          ,Total    = count(*)
          ,Complete = sum(case when D.Status = 'Complete' then 1 else 0 end)
          ,Pending  = sum(case when D.Status = 'Pending'  then 1 else 0 end)
          ,PctCmpl  = format(sum(case when D.Status = 'Complete' then 1.0 else 0.0 end)/count(*),'##0.00%')
     From  #TempHier H
     Join  (Select _R1=B.R1,A.* From @Table A Join #TempHier B on A.ID=B.ID) D on D._R1 between H.R1 and H.R2
     Group By H.R1
          ,H.R2
          ,H.Lvl
          ,H.ID
          ,H.ParentID
     Order By 1

暂时返回#Temp 表中的层级。注意 R1 和 R2,我称它们为范围键。可以通过这些键选择和聚合数据(无需递归)

R1  R2  Lvl ID  ParentID
1   4   1   101 NULL
2   2   2   1   101
3   3   2   2   101
4   4   2   3   101
5   6   1   102 NULL
6   6   2   4   102

非常简单的示例:说明将数据向上滚动。

R1  R2  Lvl ID  ParentID    Total   Complete    Pending PctCmpl
1   4   1   101 NULL        4       2           1      50.00%
2   2   2   1   101         1       0           1      0.00%
3   3   2   2   101         1       1           0      100.00%
4   4   2   3   101         1       1           0      100.00%
5   6   1   102 NULL        2       1           0      50.00%
6   6   2   4   102         1       1           0      100.00%

范围键的真正美妙之处在于,如果您知道一个 ID,您就知道它存在于何处(所有后代和祖先)。

【讨论】:

  • 一个 127K 点层级需要 12 秒来构建。我不能和 770K 说话
  • 如果有帮助,这里有一个更强大的例子stackoverflow.com/questions/37954697/…
  • 谢谢。 Total Complete PctCmpl 和 R1 和 R2 代表什么? (对不起这个愚蠢的问题)
  • 只是说明如何在一个查询中聚合数据。更好的视图是我在提供的链接上所做的屏幕截图。
  • 所以简而言之,我应该在树发生变化的任何时候创建一个层次结构表?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多