#sql #sql-server
#sql #sql-сервер
Вопрос:
У меня есть таблица местоположений, каждое из которых может иметь родительское местоположение
LocationId | ParentLocationId
-----------------------------
1 null
2 1
3 2
4 2
Мне удалось создать рекурсивный CTE, который дает мне идентификатор родительского местоположения (плюс исходный идентификатор местоположения) для любого заданного идентификатора местоположения
WITH GetLocationParents AS
(
select [LocationId], [ParentLocationId] from Locations
where LocationId = 3
UNION ALL
select i.[LocationId], i.[ParentLocationId]
from Locations i
join GetLocationParents cte on cte.ParentLocationId = i.LocationId
)
SELECT [ParentLocationId] FROM GetLocationParents
WHERE [ParentLocationId] is not NULL;
например where LocationId = 3
, вернет:
ParentLocationId
----------------
3
2
1
В другой таблице у меня есть запрос, который будет возвращен LocationId
как одно из полей:
select exi.PersonId, exi.LocationId from Persons e
left join PersonHasLocations exi on e.PersonId = exi.PersonId
left join Locations i on exi.LocationId = i.LocationId
Который с предложением where вернет что-то вроде:
PersonId | LocationId
---------------------
100 3
Я пытаюсь объединить эти запросы, чтобы получить результат:
PersonId | LocationId
---------------------
100 3
100 2
100 1
Я пробую следующее, но он по-прежнему возвращает только первую строку:
WITH
GetLocationParents AS
(select [LocationId], [ParentLocationId] from Locations
--where LocationId = 3
UNION ALL
select i.[LocationId], i.[ParentLocationId]
from Locations i inner join GetLocationParents cte
on cte.ParentLocationId = i.LocationId),
GetPersons AS
(select exi.PersonId, exi.LocationID from Persons e
left join PersonHasLocations exi on e.PersonID = exi.PersonId
left join Locations i on exi.LocationId = i.LocationID)
SELECT * FROM GetLocationParents gip
INNER JOIN GetPersons ge on ge.LocationId = gip.LocationID
WHERE ge.PersonId = 100
Возможно ли объединить рекурсивный запрос с обычным запросом, подобным этому?
Ответ №1:
Я предполагаю, что у вас есть небольшая ошибка в вашем cte. Я бы предложил изменить запрос следующим образом:
DECLARE @t TABLE (
LocationId int,
ParentLocationId int
)
INSERT INTO @t VALUES
(1, NULL)
,(2, 1)
,(3, 2)
,(4, 2)
;WITH GetLocationParents AS
(
select [LocationId] AS k, [LocationId], [ParentLocationId] from @t
UNION ALL
select k, i.[LocationId], i.[ParentLocationId]
from GetLocationParents cte
join @t i on cte.ParentLocationId = i.LocationId
)
SELECT *
FROM GetLocationParents
WHERE k = 3
При этом вы получаете список со значением, которое вы фильтруете в первом столбце, и все зависящие «уровни» выше этого во втором столбце. Затем это можно использовать для присоединения ко второй таблице.
Имейте в виду, что — в зависимости от вашего количества уровней — вам придется позаботиться MAX RECUSRSION
.
Комментарии:
1. Это отлично работает. Я не совсем понимаю, зачем мне нужен 2-й идентификатор местоположения,
k
но он работает!2. Устанавливается
k
один раз и больше не изменяется в вашей рекурсии — в противном случае locationid будет меняться с каждой итерацией, и вы потеряете начальную отправную точку.3. Ах, конечно! Таким образом, он устанавливается в первой части объединения и просто выбирается как тот же в последней части, в то время
LocationId
как циклически перебирается через все его варианты. Фантастика. Большое спасибо