2012-05-08 1 views
14

У меня есть таблица, которая содержит иерархию данных - что-то вроде:SQL Server CTE -Find top parentID для каждого дочернего 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

Я создал рекурсивные ОТВА, который находит мне верхнюю 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 
     ) 

Проблема:

Я выполнил КТР с явнымchildID значение, чтобы начать с (214)! Так что это дает мне значение только для 214. CTE выполняет рекурсивную часть и находит topParent для childID.

но Я хочу ForEach row in the Table - выполнить КТР со значением childID!

Я пытался сделать это с CROSS APPLY:

Что-то вроде:

select * from myTable Cross Apply (
            ;WITH cte AS (....) 
           ) 

но ИМХО (из моего тестирования !!) - его невозможно.

Другая идея размещения рекурсивного CTE в UDF имеет штраф за производительность (проблема udf, как мы знаем).

Как создать этот запрос, чтобы он действительно работал? (или некоторое близкое решение)?

вот что я пытался

https://data.stackexchange.com/stackoverflow/query/edit/69458

ответ

15

Не уверен, что я понимаю, что вы ищете, но это может быть это.

;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.

+0

ive добавил экран печати для желаемого вывода. –

+0

@RoyiNamir - Мой запрос на SE-Data возвращает то, что вы хотите. Я просто добавил столбцы «name» и «parentID». –

+0

УБЕДИТЕСЬ, ЧТОБЫ ЗНАЧИТЬ, СЧИТАЮТ, ЧТО ВЫ НАЧИЛИСЬ С ВЕРХНЕГО - И НЕ ЛИСТЫ ... В чем же логика здесь (хотя и работает)? почему бы нам не начать с листьев по направлению к самым верхним листьям? –

19

Не можете вы сделать что-то вроде этого?

;WITH cte AS (....) 
SELECT 
    * 
FROM 
    cte 
CROSS APPLY 
    dbo.myTable tbl ON cte.XXX = tbl.XXX 

Поместите CROSS APPLYпосле определения КТР - в заявлении один SQL, который ссылается на КТР. Разве это не работало?

OR: - переверните свою логику - сделайте «сверху вниз» 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 

Теперь, если вы выбрали конкретный дочерний узел из этого вывода КТР, вы всегда будете получать все Infos вам нужно - в том числе «уровень» ребенка и его верхнего уровня родительского узла ,

+0

Я не думаю, что это будет wiork - с КТР знает, как выполнить и найти один (!!) сверху отца для ID. обратите внимание на значение 214 ..... как я отправлю Foreach ID => Cte.DoWorkFor (ID)? –

+0

@RoyiNamir: с помощью этого 'ChildParent' CTE - вы не можете просто сделать SELECT * FROM ChildParent WHERE ID = 214' и получить то, что вам нужно? –

1

У меня еще нет времени, чтобы заглянуть в ваш вопрос, и я не уверен, понял ли я вашу проблему, но разве вы не могли использовать этот 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 
+0

Спасибо, что ответили. Тим. проблема (как мы знаем) tha t udf снижает производительность (в то время как встроенный вызов ...), поэтому я стараюсь избегать такого решения. проблема (в общем): у меня есть таблица с 'id' и' parentId'. foreach line в (выберите * from ...) - мне нужен другой столбец, который имеет самое верхнее значение для отца. - Ив пытался с помощью Cross Aply - исключить использование рекурсивного движка (CTE) с помощью Cross Apply). –

+0

ive добавил экран печати к желаемому выходу. –

-1
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 
+0

, пожалуйста, объясните свой ответ, а не просто дайте ему – ArtB

+0

У меня есть выражение без выражения CTE, а затем с помощью объединений, чтобы получить шаг пошагового родительского элемента для дочернего элемента, а затем более важный. Общие выражения таблиц были введены в SQL Server 2005 не на сервере 2000, поэтому использование присоединяется, чтобы получить значения, это основной способ получить родительский элемент для дочернего значения. – Bharani

0
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 

Я с использованием без выражения КТР, а затем с помощью объединений, чтобы получить шаг за шагом родителей для ребенка, а затем более важные общих табличных выражений были введены в SQL Server 2005 не в сервере 2000 поэтому использование объединений чтобы получить значения это основной способ, чтобы получить ParentID для значения дочернего

-1
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 
-1
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 
+0

Это решение не отвечает на вопрос. Исходные данные не имеют 'NULL' в' ParentID'. Предлагаемый запрос не дает желаемого результата. –

0

enter image description here

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 
0

Рассмотрите данные примера и соответствующий SQL для доступа к дочерним записям вместе с их верхним родителем.

Sample 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 
Смежные вопросы