T-SQL - запрос иерархической таблицы с промежуточным итогом (в каждой строке есть количество элементов) - PullRequest
0 голосов
/ 21 декабря 2010

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

CREATE TABLE #CPTree (UID int, ParentID int, Name nvarchar (150), ItemCount int)

INSERT INTO #CPTree VALUES ('1', '0', 'Vehicles', '0')

INSERT INTO #CPTree VALUES ('2', '1', 'Bikes', '10')

INSERT INTO #CPTree VALUES ('3', '1', 'Cars', '20')

INSERT INTO #CPTree Значения ('5', '2', 'Bike Make 1', '0')

INSERT INTO #CPTree Значения ('6', '2', 'Bike Make 2', '5')

INSERT INTO #CPTree Значения ('7', '3', 'Car Make 1', '0')

INSERT INTO #CPTree Значения ('8', '3', 'Car Make 2', '0')

INSERT INTO #CPTree Значения («9», «5», «Модель велосипеда A», «7»)

INSERT INTO #CPTree Значения («10», «5», «Модель B велосипеда», «1»)

INSERT INTO #CPTree Значения («11», «7», «Модель автомобиля D», «4»)

INSERT INTO #CPTree Значения («12», «8», «Модель автомобиля X», «2»)

- Получить уровень категории / дочерних категорий с помощью CTE

; с HCTE (CategoryID, ParentID, Name, itemcount, Level) как (выберите UID, ParentID, Name, itemcount, 0 как уровень от #CPTree c, где c.UID = 3 - Категория авто

объединить все выбрать c.UID, c.ParentID, c.Name, c.itemcount, ch.Level + 1 из #CPTree c внутренним соединением HCTE ch на ch.CategoryID = c.ParentID)

ВЫБРАТЬ * ИЗ HCTE

DROP TABLE # CPTree

- конец

Я хотел бы получить общее количество элементов для каждой категории, например, категория «Автомобили» верхнего уровня (количество элементов 20) имеет 2 детей (количество элементов 0 для обоих), и у каждого из них есть дети (2 и 4 пункта считаются с уважением). Общее количество предметов для Автомобилей будет равно 26. Аналогично, общее количество предметов для Автомобиля Марка 1 (CategoryID 7) будет равно 4 (сумма от его ребенка - CategoryID 11). Результат вернет это:

CategoryID | ParentID | Имя | ItemCount | Уровень | TotalItemCount

3 | 1 | Автомобили | 20 | 0 | 26

7 | 3 | Автомобиль Марка 1 | 0 | 1 | 4

8 | 3 | Автомобиль Марка 2 | 0 | 1 | 2

12 | 8 | Модель автомобиля X | 2 | 2 | 2

11 | 7 | Модель автомобиля D 4 | 2 | 4

Это позволит мне увидеть, есть ли в подкатегориях какие-либо элементы в их подкатегориях, но не обязательно, если у них есть какие-либо элементы сами. Моя живая таблица имеет много уровней, поэтому в запросе нужно будет продолжать детализировать категории, пока он не достигнет последнего уровня. При необходимости могу привести более глубокий пример.

Любая помощь приветствуется! Спасибо

Ответы [ 3 ]

1 голос
/ 21 декабря 2010

Идея состоит в том, чтобы добавить след предков (в виде строки) для каждого узла в дереве, а затем суммировать все строки, в которых есть этот узел.

   ;with HCTE(CategoryID, ParentID, Name, itemcount, Level, trail)
    as
    (
     select UID, ParentID, Name, itemcount, 0 as Level, 
                                                  '/' + cast(UID as varchar(max))+ '/' as trail 
     from #CPTree c
     where c.UID = 3 -- Cars Category


union all

select c.UID, c.ParentID, c.Name, c.itemcount, ch.Level + 1, 
                                               trail + CAST(UID as varchar(max)) + '/'
from #CPTree c 
inner join HCTE ch on ch.CategoryID = c.ParentID
)
select CategoryID, ParentID, Name, ItemCount,
   (select SUM(ItemCount)
    from HCTE h2
    where charindex('/' + CAST(h1.CategoryID as varchar(max)) + '/', h2.trail, 1) <> 0
   )
 from hcte h1
0 голосов
/ 21 декабря 2010

Я знаю, что вы приняли ответ, но я бы серьезно посмотрел на эту технику из мира DWH.

В принятом вами запросе есть 5 предложений, и в зависимости откак / если оптимизатор материализует HCTE, вы будете читать и присоединяться к одной и той же таблице несколько раз.

Эта методика по сути создает «иерархический индекс».Как и любой другой индекс, вы тратите время на вставку / обновление / удаление, чтобы сэкономить время при запросе.И, как и любой другой индекс, вы тоже тратите немного места.

В конце концов, у вас есть чрезвычайно быстрый способ получить точные ответы, которые вы хотите.

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

0 голосов
/ 21 декабря 2010

В вашем простом примере замените оператор выбора на выбор ниже.

SELECT , (выберите COUNT () из HCTE b, где a.Level + 1 = b.Level) Дети из HCTE a

Это очень просто, но если у вас много уровней, то он не будет хорошо масштабироваться.

Но вы можете ссылаться на CTE несколько раз в операторе выбора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...