Sql 具有高扫描计数和逻辑读取的递归CTE
我试图在递归CTE下进行优化,但运气不佳。该表有5079条记录Sql 具有高扫描计数和逻辑读取的递归CTE,sql,sql-server,tsql,sql-server-2012,query-performance,Sql,Sql Server,Tsql,Sql Server 2012,Query Performance,我试图在递归CTE下进行优化,但运气不佳。该表有5079条记录 ;WITH CTE_REC AS ( SELECT ID , ParentId , ID as ChildId , IsActive FROM #temp UNION ALL SELECT C.ID , C.ParentId
;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毫秒(带索引/不带索引)。您是否尝试在临时表上创建聚集索引。您创建的索引是非聚集索引,这意味着您的临时表仍然是一个堆,因此扫描计数很高,因为查询将在堆中进行扫描,以查找ChildID、IsActive等的键 在临时表上的ID、ParentID上创建聚集索引(创建聚集索引)。
然后为ParentID、ID、ChildID和IsActive添加覆盖非聚集索引。您可能需要测试此NCI,因为最好将ID移到末尾。对于服务器来说,递归只是很多步骤 我会在ID上放一个聚集的PK 在ParentID到ID之间放置FK可能会有帮助,而且可能会造成伤害 在锚点中,您可以将ParentId不为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;
您的定位不太正确,您需要将顶层限制为非子项的行:
;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
你应该减少锚的行数。在ParentId不为NULL的位置添加可能是一个不错的选择,但这实际上取决于您的数据和您需要的内容。为什么不使用hierarchyid
而不是递归?我不知道hierarchyid。你能给我一些关于hierarchyid的链接吗?看起来你的查询是错误的。非常高的Cardianity估计值。你应该改进你的锚定和递归。奇怪的格式。你聚集在什么上?身份证件你还在使用你创建的NCI吗?尝试为ParentID、ID、ChildID、IsActive添加NCI,而不是上面描述的NCI否。我删除了NCI并创建了ID为ParentID的CI。没有用。它们不多,而且处理速度很快。问题是他们没有被直接报道。@狗仔队不确定我是否理解这个评论。请提供一些样本数据。不将锚点仅限于parentid为null的行的问题是,您将多次报告相同的行。e、 g.如果A->B->C,你将报告这个,B->C和C。我不同意。我在答复中公布了数据。如果添加空值,则会得到不同的报告。如果要修剪链条,这只会修剪链条的顶部。我没有投你反对票。我认为这是有效的输入。@狗仔队您的加入是不正确的。如果您使用“on cte.id=e.mgr”作为加入条件,那么您可以添加where。。不要为你的锚定为null,然后锚定得到正确的报告。在您的查询中,id 2会被报告两次。我不想和您争论。我已经运行了查询。