【发布时间】:2015-06-12 07:56:43
【问题描述】:
我有一个自引用表Foo
[Id] int NOT NULL,
[ParentId] int NULL, --Foreign key to [Id]
[Type] char(1) NOT NULL
[Id] 是聚簇主键,在[ParentId] 和[Type] 列上建立索引。
假设层次结构的最大深度为 1(子节点不能有子节点)。
我想获得满足以下条件的所有 Foo 行:
- 类型是 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'
从实际执行计划仅限于查看 650,000 行中的前 10,000 行
如果我从查询中删除 --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';
但是,如果我尝试添加 Sibling-Child-Parent OR 语句,则会达到最大递归级别 100。
【问题讨论】:
-
@ypercube 根据所需的结果(在 sqlfiddle 上),是的,它确实
标签: sql sql-server hierarchy