2012-05-08 33 views
14

我有一個包含層次結構數據的表格 - 是這樣的:SQL Server CTE - 查找頂級parentID forEach childID?

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執行遞歸部分併爲childID找到topParent。

我想ForEach row in the Table - 與childID值執行CTE!

我試圖與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數據的查詢返回你想要的。我剛添加了'name'和'parentID'列。 –

+0

我很高興聽到你爲什麼決定從最頂層開始 - 而不是葉子....這裏的邏輯是什麼(雖然它的工作)?爲什麼我們不能從葉子開始走向最頂層的葉子? –

19

你能不能做這樣的事?

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

CROSS APPLY後CTE定義 - 成指回CTE的一個SQL語句。這不會工作?

OR: - 翻轉你的邏輯 - 做一個「自上而下」的CTE,先選擇頂層節點,然後遍歷層次結構。這樣,您就可以輕鬆地確定了「頂級的父親」中的遞歸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 

現在,如果你選擇這個CTE輸出特定的子節點,您總能獲得你需要的所有相關信息 - 包括孩子的「級別」,其頂級父節點。

+0

我不認爲它會wiork - 因爲CTE知道如何執行,並找到一個(!!)頂部的父親ID。注意214值.....我將如何發Foreach ID => Cte.DoWorkFor(ID)? –

+0

@RoyiNamir:用這個'ChildParent' CTE - 你不能只是做'SELECT * FROM ChildParent WHERE ID = 214'並且得到你需要的東西嗎? –

1

我還沒有時間進一步研究你的問題,我不確定我是否理解你的問題,但是你不能使用這個svf來獲得最高父親的ID嗎?

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

感謝您的回答。恬。這個問題(正如我們所知道的那樣)會降低性能(同時內聯調用...),這就是爲什麼我傾向於避免這種解決方案。問題(一般):我有'id'和'parentId'的表。 (select * from ...) - 我需要另一個具有最高父值的列。 - 我嘗試過Cross Aply - Vut遞歸引擎(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 

我已經使用了無CTE表達式,然後用連接來獲取一步一步的孩子父母,然後更重要的公用表表達式在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

此解決方案不回答問題。原始數據在'ParentID'中沒有'NULL'。建議的查詢不會產生所需的輸出。 –

0

enter image description here

0123基本途徑
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