我在SQL Server 2008 R2中定义了一个简单的临时表,表示父子关系。可以有多个级别的层次结构(例如,最多10个)。我正在使用CTE在我的表中找到子级至少在级别3中的子级 - 换句话说至少有一个父级和祖父级。
这是一个演示我正在使用的设置和CTE的脚本:
set nocount on
create table #linkage(entity_key bigint, parent_key bigint)
--alter table #linkage add foreign key (parent_key) references #linkage(entity_key)
insert into #linkage values(1, 1), (2, 2), (3, 3), (4, 1), (5, 4), (6, 5)
print 'all data:' select * from #linkage
print 'level 3+ descendents:'
;with r(entity_key, parent_key, level) as
(
select entity_key, parent_key, 1
from #linkage
where entity_key = parent_key
union all
select p.entity_key, r.parent_key, r.level + 1
from #linkage p
inner join r on p.parent_key = r.entity_key
where p.entity_key <> r.entity_key
)
select entity_key, parent_key as ultimate_parent_key
from r
where r.level > 2
正确输出以下内容:
all data:
entity_key parent_key
-------------------- --------------------
1 1
2 2
3 3
4 1
5 4
6 5
level 3+ descendents:
entity_key ultimate_parent_key level
-------------------- -------------------- -----------
5 1 3
6 1 4
问题是我需要这个来处理大型数据集。当我针对1200万行运行时,需要3分钟才能完成,我希望能够大大减少。
我尝试过创建群集和非群集索引(entity_key),(entity_key,parent_key)等的各种组合,但似乎没有任何帮助(事实上,有些似乎会减慢它的速度)。
这是针对没有索引的1200万行的执行计划:
|--Filter(WHERE:([Recr1014]>(2)))
|--Index Spool(WITH STACK)
|--Concatenation
|--Compute Scalar(DEFINE:([Expr1015]=(0)))
| |--Compute Scalar(DEFINE:([Expr1004]=(1)))
| |--Table Scan(OBJECT:([tempdb].[dbo].[#linkage]), WHERE:([tempdb].[dbo].[#linkage].[entity_key]=[tempdb].[dbo].[#linkage].[parent_key]))
|--Assert(WHERE:(CASE WHEN [Expr1017]>(100) THEN (0) ELSE NULL END))
|--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1017], [Recr1008], [Recr1009], [Recr1010]))
|--Compute Scalar(DEFINE:([Expr1017]=[Expr1016]+(1)))
| |--Table Spool(WITH STACK)
|--Compute Scalar(DEFINE:([Expr1011]=[Recr1010]+(1)))
|--Filter(WHERE:([tempdb].[dbo].[#linkage].[entity_key] as [p].[entity_key]<>[Recr1008]))
|--Index Spool(SEEK:([p].[parent_key]=[Recr1008]))
|--Table Scan(OBJECT:([tempdb].[dbo].[#linkage] AS [p]))
以下是XML格式的相同计划,以防您遇到这种情况:
我还应该注意,这个盒子有12个CPU,所以如果有一些方法可以引入一些并行性,那么这可能有所帮助。
有人可以推荐一种方法来加快速度吗?
答案 0 :(得分:2)
您是否尝试过parent_key
上的索引并将entity_key
添加为包含的列?
使用NULL父节点标记根节点,而不是指向自己,应该有所帮助:
declare @linkage table (entity_key bigint, parent_key bigint null)
insert into @linkage values
(1, NULL), (2, NULL), (3, NULL), (4, 1), (5, 4), (6, 5), (7, 3), (8, 7), (9, 5)
;with r(entity_key, immediate_parent, root, level) as
(
-- Faster search for NULL to find roots.
select entity_key, entity_key as immediate_parent, entity_key as root, 1
from @linkage
where parent_key is NULL
union all
-- No WHERE clause needed.
select p.entity_key, r.entity_key, r.root, r.level + 1
from r inner join
@linkage as p on p.parent_key = r.entity_key
)
select *
from r