Иерархический CTE с дополнительным столбцом сортировки на уровень - PullRequest
3 голосов
/ 24 декабря 2010

CTE немного новы для меня, поэтому я надеюсь, что кто-то может помочь со следующим, который я написал, который возьмет таблицу категорий и построит из нее иерархию для отображения. Я знаю, что подобные вещи спрашивают постоянно, но я думаю, что моя ситуация с сортировкой делает ее немного уникальной.

Я ожидаю, что ряд предложений будет использовать HierarchyID, но, к сожалению, это не вариант по длинному списку причин, которые здесь не актуальны. Решение, которое я придумала, хотя и работает и дает мне ожидаемые данные, но мне интересно, есть ли лучший / более элегантный способ сделать это.

Основные требования следующие:

  1. Категории могут иметь неограниченное количество детей
  2. Категории могут иметь неограниченное количество уровней глубины
  3. Категории с одним и тем же родителем будут отсортированы на основе поля «сортировка». Если он не указан (по умолчанию 0) или совпадает с другой категорией, он будет отсортирован в алфавитном порядке.

Определение таблицы:

CREATE TABLE [dbo].[TreeTest]
(
    [id] [int] NOT NULL,
    [parent] [int] NULL,
    [title] [varchar](50) NOT NULL,
    [sort] [int] NOT NULL
)
GO

ALTER TABLE [dbo].[TreeTest] ADD  CONSTRAINT [DF_TreeTest_sort]  DEFAULT ((0)) FOR [sort]
GO

Вставить операторы:

INSERT TreeTest(id,parent,title,sort) VALUES('1',NULL,'Parent 1','0')
INSERT TreeTest(id,parent,title,sort) VALUES('2',NULL,'Parent 2','0')
INSERT TreeTest(id,parent,title,sort) VALUES('3',NULL,'Parent 3','2')
INSERT TreeTest(id,parent,title,sort) VALUES('4',NULL,'Parent 4','1')
INSERT TreeTest(id,parent,title,sort) VALUES('5','1','Child 1a','0')
INSERT TreeTest(id,parent,title,sort) VALUES('6','2','Child 2a','0')
INSERT TreeTest(id,parent,title,sort) VALUES('7','3','Child 3a','0')
INSERT TreeTest(id,parent,title,sort) VALUES('8','1','Child 1b','1')
INSERT TreeTest(id,parent,title,sort) VALUES('9','1','Child 1c','2')
INSERT TreeTest(id,parent,title,sort) VALUES('10','1','Child 1d','1')
INSERT TreeTest(id,parent,title,sort) VALUES('11','6','Child 2a 1','0')
INSERT TreeTest(id,parent,title,sort) VALUES('12','6','Child 2a 2','1')
INSERT TreeTest(id,parent,title,sort) VALUES('13','6','Child 2a 3','0')
INSERT TreeTest(id,parent,title,sort) VALUES('14','6','Child 2a 4','2')

CTE:

WITH TreeList (id, parent, title, sort, title_path, level_id, level_id_path) as
(
    SELECT p.id, 
           p.parent, 
           p.title, 
           p.sort,
           CONVERT(nvarchar(max), p.title), 
           ROW_NUMBER() OVER(PARTITION BY parent ORDER BY p.sort, p.title), 
           CAST(ROW_NUMBER() OVER(PARTITION BY parent ORDER BY p.sort) AS varchar(max))
        FROM TreeTest p
        WHERE p.parent is null
    UNION ALL
    SELECT c.id, 
           c.parent, 
           c.title, 
           c.sort,
           r.title_path + '/' + c.title, 
           ROW_NUMBER() OVER(PARTITION BY c.parent ORDER BY c.sort, c.title), 
           CONVERT(varchar(max), r.level_id_path + '.' + CAST(ROW_NUMBER() OVER(PARTITION BY c.parent ORDER BY c.sort, c.title) AS VARCHAR))
        FROM TreeTest AS c
        INNER JOIN treelist AS r
            ON c.parent = r.id
)
SELECT *
FROM TreeList
ORDER BY level_id_path

Вывод (я подумал, что изображение было самым простым способом показать вывод)

output

Опять же, это работает в соответствии со спецификациями, которые у меня есть, но я не уверен в эффективности и в том, есть ли лучший способ сделать это. Когда я смотрю на план выполнения для этого, кажется, что самым дорогим является сканирование с сортировкой / индексированием, но это кажется ожидаемым, учитывая отсутствие индексов в этом примере. Если у кого-то есть какие-либо материалы, это будет с благодарностью.

Ответы [ 2 ]

1 голос
/ 03 июня 2013
order by case when parent=0 then parentid else id end

здесь id означает первичный ключ в таблице

0 голосов
/ 25 июня 2012

Этот рекурсивный CTE в значительной степени является самым чистым вариантом в SQL Server. Если бы это был Oracle, вы могли бы использовать CONNECT BY, но в SQL Server нет эквивалента, поскольку вы уже заявили, что не можете использовать HierarchyID.

...