SQL-запрос: иерархическое объединение

У меня есть таблица, которая определяет иерархию:

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;

Диаграмма иерархии для этих данных:

Hierarchy diagram

Я хочу написать запрос, который будет возвращать одну строку для любого заданного [ id] значение. Строка должна содержать информацию [id] и [parentID] этой строки. Он также должен содержать поля [largeData1 ... n]. Однако, если поле largeData имеет значение null, оно должно проходить по иерархии до тех пор, пока не будет найдено ненулевое значение для этого поля. Короче говоря, она должна функционировать как функция объединения, за исключением иерархии строк вместо набора столбцов.

Пример:

Где [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, если это поможет. Я еще не исследовал этот маршрут.

8
задан Alex 13 May 2015 в 19:13
поделиться

1 ответ

Я придумал следующее:

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

По сути, начните с целевого узла и поднимитесь по дереву, заменяя пустые столбцы ненулевыми значениями, если и когда они будут найдены.

(Извините, но я не занимаюсь XML по выходным.)

6
ответ дан 5 December 2019 в 22:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: