8

階層を定義するテーブルがあります。

Create Table [example] (
    id          Integer   Not Null Primary Key,
    parentID    Integer       Null,
    largeData1  nVarChar(max) Null,
    largeData2  nVarChar(max) Null);
    -- largeData3...n also exist

Insert Into [example] (id, parentID, largeData1, largeData2)
Select 1, null, 'blah blah blah', null          Union
Select 2,    1, null,             null          Union
Select 3,    1, 'foo bar foobar', null          Union
Select 4,    3, null,             'lorem ipsum' Union
Select 5,    4, null,             null;

このデータの階層図:

階層図

特定の [id] 値に対して単一の行を返すクエリを作成したいと考えています。行には、その行の [id] および [parentID] 情報が含まれている必要があります。[largeData1...n] フィールドも含まれている必要があります。ただし、largeData フィールドが null の場合は、そのフィールドの null 以外の値が検出されるまで、階層を上に移動する必要があります。つまり、一連の列ではなく行の階層を横断することを除いて、coalesce 関数のように機能する必要があります。

例:

[id] = 1 の場合:

id:          1
parentID:    null
largeData1:  blah blah blah
largeData2:  null

[ID] = 2

id:          1
parentID:    1
largeData1:  blah blah blah
largeData2:  null

[ID] = 3

id:          3
parentID:    1
largeData1:  foo bar foobar
largeData2:  null

[ID] = 4

id:          4
parentID:    3
largeData1:  foo bar foobar
largeData2:  lorem ipsum

[ID] = 5

id:          5
parentID:    4
largeData1:  foo bar foobar
largeData2:  lorem ipsum

これまでのところ、私はこれを持っています:

Declare @id Integer; Set @id = 5;

With heirarchy
    (id, parentID, largeData1, largeData2, [level])
As (
    Select id, parentID, largeData1,
           largeData2, 1 As [level]
    From example
    Where id = @id

    Union All

    Select parent.id, parent.parentID,
           parent.largeData1,
           parent.largeData2,
           child.[level] + 1 As [level]
    From example As parent
    Inner Join heirarchy As child
        On parent.id = child.parentID)

Select id, parentID,
   (Select top 1 largeData1
    From heirarchy
    Where largeData1 Is Not Null
    Order By [level] Asc) As largeData1,

   (Select top 1 largeData2
    From heirarchy
    Where largeData2 Is Not Null
    Order By [level] Asc) As largeData2

From example
Where [id] = @id;

これにより、探している結果が返されます。ただし、クエリ プランによると、プルバックする largeData フィールドごとに階層を個別に通過させています。

これをより効率的にするにはどうすればよいですか?

これは明らかに、より複雑な問題の単純化されたバージョンです。最後のクエリはデータを XML 形式で返すため、FOR XML 句を含むソリューションは問題ありません。

これが役立つ場合は、CLR 集計関数を作成できます。私はまだそのルートを探索していません。

4

1 に答える 1

6

私はこれを思いついた:

DECLARE @Id  int

SET @Id = 5


;WITH cte (Id, ParentId, SaveParentId, LargeData1, LargeData2)
 as (--  The "anchor", your target Id
     select
        ex.Id
       ,ex.ParentId
       ,ex.ParentId  SaveParentId  --  Not changed throughout the CTE
       ,ex.LargeData1
       ,ex.LargeData2
      from Example ex
      where ex.Id = @Id
     union all select
                 cte.Id
                ,ex.ParentId
                ,cte.SaveParentId  --  Not changed throughout the CTE
                 --  These next are only "reset" if they are null and a not-null
                 --  value was found at this level 
                ,isnull(ex.LargeData1, cte.LargeData2)  
                ,isnull(ex.LargeData2, cte.LargeData2)
      from Example ex
       inner join cte
        on cte.ParentId = ex.Id)
 select
   Id
  ,SaveParentId     ParentId
  ,max(LargeData1)  LargeData1
  ,max(LargeData2)  LargeData2
 from cte
 group by Id, SaveParentId

基本的に、ターゲットノードから開始してツリーを上に移動し、null列が見つかった場合は、null列をnull以外の値に置き換えます。

(申し訳ありませんが、週末はXMLを使用しません。)

于 2010-08-21T18:56:43.613 に答える