Рекурсивный CTE с порядком

Я работаю с иерархическими данными и использую рекурсивный CTE для перечисления таких элементов:

Eletronics
    Televisions
        Tube
        LCD
        Plasma
    Portable Electronic
        MP3 Players
            Flash
        CD Player
        Two Way Radios

Мой вопрос:
Как сделать этот список упорядоченным по заголовку и соблюдая иерархию?

Как это:

Eletronics
    Portable Electronic
        CD Player
        MP3 Players
            Flash
        Two Way Radios
    Televisions
        LCD
        Plasma
        Tube

спасибо


person Onaiggac    schedule 16.08.2012    source источник
comment
Как мы можем помочь, не видя кода   -  person Bort    schedule 16.08.2012
comment
Выбор программного обеспечения базы данных и его версии оказывают значительное влияние на возможные решения. Что ты бежишь?   -  person HABO    schedule 16.08.2012
comment
возможный дубликат настоящей рекурсии с CTE?   -  person Mikael Eriksson    schedule 16.08.2012


Ответы (3)


Вот правильный CTE (только для возрастания)

;WITH CTE AS
(
SELECT  id, id_parent, name_product
        ,HierarchicalPath = CAST('\'+CAST(name_product AS VARCHAR(MAX)) AS VARCHAR(MAX)) 
FROM @tab where id_parent is null -- Starts with the first level

UNION ALL

SELECT  t.id, t.id_parent, t.name_product
        ,HierarchicalPath = CAST(c.HierarchicalPath + '\'+CAST(t.name_product AS VARCHAR(MAX)) AS VARCHAR(MAX))
FROM @tab t INNER JOIN CTE C
ON t.id_parent = C.id
)

select * from cte order by HierarchicalPath
person Onaiggac    schedule 23.08.2013

Вот код, который я почти сделал, что хочу. Теперь проблема в том, чтобы заказать по убыванию

declare @tab table(
    id int identity(1,1)
    ,id_parent int
    ,name_product varchar(100)
)

insert into @tab
select null, 'Eletronics'
union all
select 1, 'Televisions'
union all
select 2, 'Tube'
union all
select 2, 'LCD'
union all
select 2, 'Plasma'
union all
select 1, 'Portable Electronic'
union all
select 6, 'MP3 Players'
union all
select 7, 'Flash'
union all
select 6, 'CD Player'
union all
select 6, 'Two Way Radios'

;WITH CTE (id,id_parent,name_product,LEVEL,SORTKEY)AS
(
    SELECT id, id_parent, name_product, 1, CAST(name_product AS VARBINARY(MAX))
    FROM @tab where id_parent is null -- Starts with the first level

    UNION ALL

    SELECT t.id, t.id_parent, t.name_product, C.LEVEL + 1, CAST(C.SORTKEY + CAST(t.name_product AS VARBINARY(MAX)) AS VARBINARY(MAX))
    FROM @tab t INNER JOIN CTE C
    ON t.id_parent = C.id
)

select * from cte order by SORTKEY

спасибо

person Onaiggac    schedule 16.08.2012
comment
Если я могу заказать только определенный уровень, все в порядке. - person Onaiggac; 22.08.2012
comment
Измените предложение order by в своем окончательном выражении, чтобы включить столбец LEVEL, например. 'упорядочить по УРОВНЮ, СОРТИРОВКА' - person Gruff Bunny; 24.06.2013
comment
Если я это сделаю, дети не пойдут за родителями. - person Onaiggac; 26.06.2013

Вариант на основе @onaiggac (вы можете использовать его данные :-))

;WITH CTE (id, id_parent, name_product, LEVEL, SORTKEY) AS
(
    SELECT id, id_parent, name_product, 1, 
        CAST(ROW_NUMBER() OVER (ORDER BY name_product) AS VARBINARY(MAX))
    FROM @tab where id_parent is null -- Starts with the first level

    UNION ALL

    SELECT t.id, t.id_parent, t.name_product, C.LEVEL + 1, 
        C.SORTKEY + CAST(ROW_NUMBER() OVER (ORDER BY t.name_product) AS VARBINARY(MAX))
    FROM @tab t 
    INNER JOIN CTE C ON t.id_parent = C.id
)

SELECT id, id_parent, REPLICATE('   ', LEVEL - 1) + name_product, LEVEL, SORTKEY FROM CTE ORDER BY SORTKEY

Хитрость здесь заключается в использовании

ROW_NUMBER() OVER (ORDER BY name_product)

сделать «внутренний» заказ. Как и в @onaiggac, это затем составляется в двоичном формате varbinary(max)

CAST(ROW_NUMBER() OVER (ORDER BY name_product) AS VARBINARY(MAX))

который затем рекурсивно добавляется...

C.SORTKEY + CAST(ROW_NUMBER() OVER (ORDER BY t.name_product) AS VARBINARY(MAX))

Обратите внимание, что ROW_NUMBER() вернет bigint... вы можете привести его к int перед преобразованием к VARBINARY(MAX), например

CAST(CAST(ROW_NUMBER() OVER (ORDER BY name_product) AS INT) AS VARBINARY(MAX))

если вы действительно хотите... Я не думаю, что это необходимо, если только ваше дерево не действительно глубокое.

person xanatos    schedule 02.07.2015