У меня есть автореферентная таблица Foo
Выберите строки из иерархии на основе связанных узлов
[Id] int NOT NULL,
[ParentId] int NULL, --Foreign key to [Id]
[Type] char(1) NOT NULL
[Id]
является кластерными первичным ключом, индексы на [ParentId]
и [Type]
столбцов.
Предположим, что максимальная глубина 1 в иерархии (дочерние узлы не могут иметь дочерние узлы).
Я хочу, чтобы все строки Foo, которые удовлетворяют следующим образом:
- Тип является
- Имеет 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'
От фактического плана выполнения ограничивается только глядя на первые 10 000 из 650 000 строк
Если я удаляю --Парентные линии из запроса
OR [NodeB].[Id] = [Parent].[Id] --Parent
OR [NodeCD].[Id] = [Parent].[Id] --Parent
, то выполнение становится почти мгновенно, но он пропускает случаи, когда А является дочерним и имеет только один родственный
Misses this: Catches this:
B B
├A ├A
└C ├B
└C
Я пытался придумать КТР, чтобы сделать это, так как это кажется более перспективным с точки зрения производительности, но я не смог понять, как исключить те деревья, которые не удовлетворяют критериям.
КТР до сих пор
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-Родитель или заявления, я ударил максимальный уровень рекурсии 100.
@ypercube Согласно желаемым результатам (которые на sqlfiddle), да, это – Lamak