4

次のテーブル (ObjectStates) があり、ルートとルートの最初の子を取得したいと考えています。

ID    Title    ParentID
1     Draft    null
2     Green    null
3     Red      null
4     Foo      1
5     Bar      4
6     Some1    1
7     Some2    6
8     XYZ      2
9     Some3    7

次の出力が必要です。

GetState(5)
-- returns root: 1, first-child: 4
GetState(6)
-- returns root: 1, first-child: 6
GetState(7)
-- returns root: 1, first-child: 6
GetState(9)
-- returns root: 1, first-child: 6
GetState(8)
-- returns root: 2, first-child: 8

したがって、クエリを実行している階層の深さに関係なく、ルート要素と最初の子要素が常に必要です。あなたがこの木を考えるなら、私はどんなに深くても、赤と青の要素を常に求めています。

ここに画像の説明を入力

次のように「ルート」状態を取得できます。

WITH CTEHierarchy
AS (
    SELECT 
    ID
        ,0 AS LEVEL
        ,ID AS root

    FROM ObjectStates
    WHERE  ParentID IS NULL

    UNION ALL

    SELECT 
    ObjectStates.ID
        ,LEVEL + 1 AS LEVEL
        ,[root]

    FROM ObjectStates
    INNER JOIN CTEHierarchy uh ON uh.id = ObjectStates.ParentID
    )    
    SELECT [root]
    FROM CTEHierarchy
    WHERE ID = @ObjectStateID 

これにより、希望するルート結果が得られます。

GetState(5)
-- returns root: 1
GetState(9)
-- returns root: 1
GetState(2)
-- returns root: 2

そこからどのようにトラバースできますか?ルートからツリーの次の子を取得しますか? またはその逆 - ルートと最初のレベルを取得します。再帰は私の頭を壊しています。

4

2 に答える 2

2

最初に階層を上ってから、上位 2 つのレベルを取得する必要があると思います。

WITH cteHierarchy As
(
   SELECT
      ID,
      ParentID,
      0 As Level
   FROM
      ObjectStates
   WHERE
      ID = @ObjectStateID

   UNION ALL

   SELECT
      OS.ID,
      OS.ParentID,
      H.Level + 1
   FROM
      cteHierarchy As H
      INNER JOIN ObjectStates As OS
      ON H.ParentID = OS.ID
),
cteReveresedHierarchy As
(
   SELECT
      ID,
      ROW_NUMBER() OVER (ORDER BY Level DESC) As RowNumber
   FROM
      cteHierarchy
)
SELECT
   ID
FROM
   cteReveresedHierarchy
WHERE
   RowNumber In (1, 2)
;

編集
2 つの項目を 1 行で取得するには:

ルートから開始しないことを保証できる場合は、フィルターをWHERE RowNumber = 2に変更して、列を含めることができParentIDます。ただし、ルートから開始した場合は、行が 1 つしかないため、そのクエリは機能しません。

ルートからクエリを開始できるようにするには、行 2 が存在する場合は行 2 を、存在しない場合は行 1 を取得する必要があります。

WITH cteHierarchy As
(
   SELECT
      ID,
      ParentID,
      0 As Level
   FROM
      ObjectStates
   WHERE
      ID = @ObjectStateID

   UNION ALL

   SELECT
      OS.ID,
      OS.ParentID,
      H.Level + 1
   FROM
      cteHierarchy As H
      INNER JOIN ObjectStates As OS
      ON H.ParentID = OS.ID
),
cteReveresedHierarchy As
(
   SELECT
      ID,
      ParentID,
      ROW_NUMBER() OVER (ORDER BY Level DESC) As RowNumber
   FROM
      cteHierarchy
)
SELECT TOP 1
   ParentID As [root]
   ID As [FirstChild]
FROM
   cteReveresedHierarchy
WHERE
   RowNumber In (1, 2)
ORDER BY
   RowNumber DESC
;
于 2012-11-27T14:36:47.470 に答える