SQL Server CTE -Найдите верный родительскийID для каждого дочернего элемента?
У меня есть таблица, которая содержит данные иерархии - что-то вроде:
childID | parentID
____________________
1 | 5
5 | 9
9 | 20
2 | 4
3 | 7
7 | 8
8 | 8
20 | 20
4 | 4
8 | 8
желаемый результат:
![enter image description here]()
Я создал рекурсивный CTE, который находит меня топ fatherID
.
Что-то вроде:
;WITH cte AS (
SELECT a.childID
,a.parentID
,1 AS lvl
FROM [Agent_Agents] a
WHERE a.childID = 214 //<==== value to begin with !! - thats part the problem
UNION ALL
SELECT tmp.childID
,tmp.parentID
,cte.lvl+1
FROM [Agent_Agents] tmp
INNER JOIN cte ON tmp.childID = cte.parentID
WHERE cte.childID<>cte.parentID
)
SELECT *
FROM cte
WHERE lvl = (
SELECT MAX(lvl)
FROM cte
)
Проблема:
Я выполнил CTE с явным childID
значением для начала (214)!
Поэтому он дает мне значение только для 214.
CTE выполняет рекурсивную часть и находит topParent для childID.
но
Я хочу ForEach row in the Table
- выполнить CTE со значением childID
!
Я попытался сделать это с помощью CROSS APPLY
:
Что-то вроде:
select * from myTable Cross Apply (
;WITH cte AS (....)
)
но IMHO (из моего тестирования!!) - его невозможно.
Другая идея поместить рекурсивный CTE в UDF имеет ограничение производительности (проблема udf, как мы знаем).
Как я могу создать этот запрос, чтобы он действительно работал? (или некоторое близкое решение)?
вот что я пробовал
https://data.stackexchange.com/stackoverflow/query/edit/69458
Ответы
Ответ 1
Не уверен, что я понимаю, что вы ищете, но может быть и так.
;WITH c
AS (SELECT childid,
parentid,
parentid AS topParentID
FROM @myTable
WHERE childid = parentid
UNION ALL
SELECT T.childid,
T.parentid,
c.topparentid
FROM @myTable AS T
INNER JOIN c
ON T.parentid = c.childid
WHERE T.childid <> T.parentid)
SELECT childid,
topparentid
FROM c
ORDER BY childid
SE-Data
Это то же самое, что answer marc_s с той разницей, что я использую вашу переменную таблицы и факт, что у вас есть childID = parentID
для корневых узлов, где ответ marc_s имеет parent_ID = null
для корневых узлов. По-моему, лучше иметь parent_ID = null
для корневых узлов.
Ответ 2
Разве вы не можете сделать что-то подобное?
;WITH cte AS (....)
SELECT
*
FROM
cte
CROSS APPLY
dbo.myTable tbl ON cte.XXX = tbl.XXX
Поместите CROSS APPLY
после определение CTE - в один оператор SQL, который ссылается на CTE. Разве это не работало?
ИЛИ: - переверните свою логику - сделайте "сверху вниз" CTE, который сначала выбирает узлы верхнего уровня, а затем выполняет итерацию через hiearchy. Таким образом, вы можете легко определить "отец верхнего уровня" в первой части рекурсивного CTE - что-то вроде этого:
;WITH ChildParent AS
(
SELECT
ID,
ParentID = ISNULL(ParentID, -1),
SomeName,
PLevel = 1, -- defines level, 1 = TOP, 2 = immediate child nodes etc.
TopLevelFather = ID -- define "top-level" parent node
FROM dbo.[Agent_Agents]
WHERE ParentID IS NULL
UNION ALL
SELECT
a.ID,
ParentID = ISNULL(a.ParentID, -1),
a.SomeName,
PLevel = cp.PLevel + 1,
cp.TopLevelFather -- keep selecting the same value for all child nodes
FROM dbo.[Agent_Agents] a
INNER JOIN ChildParent cp ON r.ParentID = cp.ID
)
SELECT
ID,
ParentID,
SomeName,
PLevel,
TopLevelFather
FROM ChildParent
Это даст вам узлы что-то вроде этого (на основе ваших данных образца, немного расширенных):
ID ParentID SomeName PLevel TopLevelFather
20 -1 Top#20 1 20
4 -1 TOP#4 1 4
8 -1 TOP#8 1 8
7 8 ChildID = 7 2 8
3 7 ChildID = 3 3 8
2 4 ChildID = 2 2 4
9 20 ChildID = 9 2 20
5 9 ChildID = 5 3 20
1 5 ChildID = 1 4 20
Теперь, если вы выберете конкретный дочерний элемент node из этого вывода CTE, вы всегда получите всю необходимую информацию, включая "уровень" дочернего элемента, и его родительский элемент верхнего уровня node.
Ответ 3
У меня еще нет времени, чтобы заглянуть в ваш вопрос, и я не уверен, понял ли я вашу проблему, но не мог ли вы использовать этот svf для получения идентификатора верхнего отца?
CREATE FUNCTION [dbo].[getTopParent] (
@ChildID INT
)
RETURNS int
AS
BEGIN
DECLARE @result int;
DECLARE @ParentID int;
SET @ParentID=(
SELECT ParentID FROM ChildParent
WHERE ChildID = @ChildID
)
IF(@ParentID IS NULL)
SET @result = @ChildID
ELSE
SET @result = [dbo].[getTopParent](@ParentID)
RETURN @result
END
Затем вы сможете найти каждого главного родителя таким образом:
SELECT ChildID
, [dbo].[getTopParent](ChildID) AS TopParentID
FROM ChildParent
Ответ 4
select distinct
a.ChildID,a.ParentID,
--isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa,
B.parentID
--,c.parentID
,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name
from myTable a
inner join myTable c
on a.parentID=c.parentID
inner join myTable b
on b.childID=a.parentID
inner join myTable d
on d.childID=b.parentID
У меня есть выражение без CTE, а затем с помощью объединений, чтобы получить шаг пошагового родительского элемента для дочернего элемента, а затем более важный. Общие выражения таблиц были введены в SQL Server 2005 не на сервере 2000, поэтому использование объединений для получения значений является основным способом для получения parentid для дочернего значения
Ответ 5
![введите описание изображения здесь]()
select dbo.[fn_getIMCatPath](8)
select Cat_id,Cat_name,dbo.[fn_getIMCatPath](cat_id) from im_category_master
Create FUNCTION [dbo].[fn_getIMCatPath] (@ID INT)
returns NVARCHAR(1000)
AS
BEGIN
DECLARE @Return NVARCHAR(1000),
@parentID INT,
@iCount INT
SET @iCount = 0
SELECT @Return = Cat_name,
@parentID = parent_id
FROM im_category_master
WHERE [cat_id] = @ID
WHILE @parentID IS NOT NULL
BEGIN
SELECT @Return = cat_name + '>' + @Return,
@parentID = parent_id
FROM im_category_master
WHERE [cat_id] = @parentID
SET @iCount = @iCount + 1
IF @parentID = -1
BEGIN
SET @parentID = NULL
END
IF @iCount > 10
BEGIN
SET @parentID = NULL
SET @Return = ''
END
END
RETURN @Return
END
Ответ 6
Рассмотрим данные примера и соответствующий SQL для доступа к дочерним записям вместе со своим главным родителем.
Пример DATA
Код SQL:
;WITH c AS (
SELECT Id, Name, ParentId as CategoryId,
Id as MainCategoryId, Name AS MainCategory
FROM pmsItemCategory
WHERE ParentId is null
UNION ALL
SELECT T.Id, T.Name, T.ParentId, MainCategoryId, MainCategory
FROM pmsItemCategory AS T
INNER JOIN c ON T.ParentId = c.Id
WHERE T.ParentId is not null
)
SELECT Id, Name, CategoryId, MainCategoryId, MainCategory
FROM c
order by Id
Ответ 7
select distinct
a.ChildID,a.ParentID,
--isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa,
B.parentID
--,c.parentID
,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name
from myTable a
inner join myTable c
on a.parentID=c.parentID
inner join myTable b
on b.childID=a.parentID
inner join myTable d
on d.childID=b.parentID
Ответ 8
With cte as
(
Select ChileId,Name,ParentId from tblHerarchy
where ParentId is null
union ALL
Select h.ChileId,h.Name,h.ParentId from cte
inner join tblHerarchy h on h.ParentId=cte.ChileId
)
Select * from cte
Ответ 9
With cteherarchy as
(
Select ChileId,Name,ParentId from tblHerarchy
where ParentId is null
union ALL
Select h.ChileId,h.Name,h.ParentId from cte
inner join tblHerarchy h on h.ParentId=cte.ChileId
)
Select * from cteherarchy