web-dev-qa-db-ja.com

SQL Server CTE-各childIDの最上位のparentIDを検索しますか?

階層データを含むテーブルがあります-次のようなものです:

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を見つける再帰CTEを作成しました。

何かのようなもの:

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

問題:

explicitchildID値を使用して(214)で始まるCTEを実行しました!したがって、214の値のみが得られます。 CTEは再帰的な部分を実行し、childIDのtopParentを見つけます。

butForEach 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

16
Royi Namir

あなたが探しているものがわかりませんが、これかもしれません。

;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-データ

これは answer by marc_s と同じですが、テーブル変数を使用する点と、marc_sによる回答があるルートノードにchildID = parentIDがあるという事実が異なります。ルートノードにはparent_ID = nullがあります。私の意見では、ルートノードにはparent_ID = nullを使用することをお勧めします。

16
Mikael Eriksson

このようなことはできませんか?

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

置く CROSS APPLYafter CTE定義-CTEを参照する1つの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出力から特定の子ノードを選択すると、子の「レベル」とその最上位の親ノードなど、必要なすべての情報が常に取得されます。

21
marc_s

私はまだあなたの質問をさらに調べる時間がなく、あなたの問題を理解したかどうかはわかりませんが、この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
1
Tim Schmelter
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なしの式を使用してから、結合を使用して子の親をステップ実行するステップを取得しました。さらに重要な共通テーブル式は、サーバー2000ではなくSQL Server 2005で導入されたため、結合を使用して値を取得するのが基本的な方法です。子値の親ID

0
Bharani

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
Goutam Panda

このサンプルデータとそれぞれのSQLを検討して、子レコードとその最上位の親にアクセスします。

サンプルデータ

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
0
BethT