我有一张自引用的表格 Foo
[Id] int NOT NULL,
[ParentId] int NULL, --Foreign key to [Id]
[Type] char(1) NOT NULL
[Id]
是集群化主键,[ParentId]
和[Type]
列上有索引。
假设层级的最大深度为1(子节点不能有子节点)。
我想要获取所有满足以下条件的Foo行:
- Type为A
- 其家族树中有B
- 其家族树中有C或D
下面使用JOIN的查询返回所需结果,但性能非常差。
SELECT DISTINCT [Main].*
FROM Foo AS [Main]
--[Main] may not be root node
LEFT OUTER JOIN Foo AS [Parent]
ON [Parent].[Id] = [Main].[ParentId]
--Must have a B in tree
INNER JOIN Foo AS [NodeB]
ON (
[NodeB].[Pid] = [Main].[Pid] --Sibling
OR [NodeB].[ParentId] = [Main].[Id] --Child
OR [NodeB].[Id] = [Parent].[Id] --Parent
)
AND [NodeB].[Type] = 'B'
--Must have a C or D in tree
INNER JOIN Foo AS [NodeCD]
ON (
[NodeCD].[Pid] = [Main].[Pid] --Sibling
OR [NodeCD].[ParentId] = [Main].[Id] --Child
OR [NodeCD].[Id] = [Parent].[Id] --Parent
)
AND [NodeCD].[Type] IN ('C', 'D')
WHERE [Main].[Type] = 'A'
从实际执行计划来看,仅查看了65万行中的前1万行。
如果我从查询中删除--Parent行。
OR [NodeB].[Id] = [Parent].[Id] --Parent
OR [NodeCD].[Id] = [Parent].[Id] --Parent
那么执行就几乎瞬间完成了,但它错过了A是一个仅有一个兄弟的子节点的情况。
Misses this: Catches this:
B B
├A ├A
└C ├B
└C
我尝试使用CTE来实现这个功能,因为从性能方面来看它似乎更有前途,但我无法想出如何排除不符合条件的树。
目前的CTE:
WITH [Parent] AS
(
SELECT *
FROM [Foo]
WHERE [ParentId] IS NULL
UNION ALL
SELECT [Child].*
FROM Foo AS [Child]
JOIN [Parent]
ON [Child].[ParentId] = [Parent].Id
WHERE [Child].[Type] = 'P'
UNION ALL
SELECT [ChildCD].*
FROM Foo AS [ChildCD]
JOIN [Parent]
ON [ChildCD].[ParentId] = [Parent].Id
WHERE [ChildCD].[Type] IN ('C', 'D')
)
SELECT *
FROM [Parent]
WHERE [Type] = 'I';
然而,如果我尝试添加兄弟-子代-父代OR语句,我会达到100次递归的最大限制。