我试图在没有运气的情况下优化下面的递归CTE。该表有5079条记录。
;WITH CTE_REC AS (
SELECT
ID
, ParentId
, ID as ChildId
, IsActive
FROM
#temp
UNION ALL
SELECT
C.ID
, C.ParentId
, H.ChildId
,H.IsActive
FROM
#temp AS C
INNER JOIN
CTE_REC H ON C.ID = H.ParentId
)
SELECT * FROM CTE_REC
上述查询的执行计划是:
IO统计数据是:
(25441 row(s) affected)
Table 'Worktable'.
Scan count 20365, logical reads 193768, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0,
lob read-ahead reads 0.
Table '#temp_______________________________________________________________________________________________________________000000001B2D'.
Scan count 2, logical reads 34, physical reads 0, read-ahead reads 17,
lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
我在临时表上创建了以下索引。
CREATE INDEX IX_TEMP ON #Temp(Id,ParentId)
创建索引后,执行计划如下所示。
索引后的IO统计数据:
Table '#temp_______________________________________________________________________________________________________________000000001B2D'.
Scan count 20364, logical reads 40776, physical reads 0, read-ahead reads 0,
lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'.
Scan count 2, logical reads 142778, physical reads 0, read-ahead reads 0,
lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
仍然在索引之后有高扫描计数和逻辑读取。 CTE返回25411行,我没有发现CPU时间的任何差异,有/无索引为400毫秒。
答案 0 :(得分:0)
该递归只是服务器的很多步骤。
我只想在ID上放置一个聚集的PK。
将父F上的FK放入ID可能会有所帮助,这可能会有所帮助。
在你可以放置where ParentId is not null
的锚点中,但是它们不会有很多,它会从报告中删除它们。
在锚点中,您只能过滤到没有人向他们报告的人。你仍然得到所有的链条。当我的老板与我的同一个链条时,在我的老板上有一个单独的链是有点愚蠢的。
计算码头的链条也很浪费。如果我们有一个共同的老板,那么我们就拥有相同的链条。这里3和6具有相同的链。在下面的示例中,您只需要锚定:
select min(e.id) as 'modelGrunt', e.mgr
from @emp e
where not exists (select 1 from @emp e1 where e1.mgr = e.id)
group by e.mgr;
根据这些信息,您可以构建每个链。运行它并实现它。这是一个更复杂的查询,但将递归行的数量减少到接近最小值。它不是一个完整的最小值,因为你可能会发疯,甚至不会重复子链。
我几乎一样,但是数量不多,这不是问题。您需要使用排序进行优化,否则结果不会以有意义的方式进行分组。这有索引搜索和索引扫描。
declare @emp table (id int primary key, mgr int);
insert into @emp values
(1, null)
, (2, 1)
, (3, 2)
, (4, null)
, (5, 4)
, (6, 2);
--select * from @emp;
; with cte as
( select e.id ori, e.id, e.mgr, cnt = 1
from @emp e
union all
select cte.ori, e.id, e.mgr, cnt + 1
from @emp e
join cte
on cte.mgr = e.id
)
select ori, id, mgr, cnt
from cte
order by ori, cnt;
答案 1 :(得分:0)
您的锚点不太正确,您需要将顶层限制为仅非子项目的行:
;WITH CTE_REC AS (
SELECT
ID
, ParentId
, ID as ChildId
, IsActive
FROM #temp
WHERE ParentId IS NULL
UNION ALL
SELECT
C.ID
, C.ParentId
, H.ChildId
,H.IsActive
FROM #temp AS C
INNER JOIN CTE_REC H
ON C.ID = H.ParentId
WHERE C.ParentId IS NOT NULL
)
SELECT * FROM CTE_REC
答案 2 :(得分:-1)
您是否尝试在临时表上创建聚簇索引?您创建的索引是非群集的,这意味着您的临时表仍然是一个堆,因此高扫描计数,因为查询将扫描堆以对ChildID,IsActive等进行密钥查找。
在临时表上的ID,ParentID上创建聚簇索引(CREATE CLUSTERED INDEX)。 然后为ParentID,ID,ChildID,IsActive添加覆盖非聚集索引。您可能需要测试此NCI,因为最好将ID移到最后。