Рекурсивный расчет для формирования дерева с использованием sql

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

Id  Name    ParentCategoryId
1   Category1   Null
2   Category1.1 1
3   Category2.1 2
4   Category1.2 1
5   Category3.1 3

ID  CateoryId   ItemId
1   5              1
2   4              2
3   5              2
4   3              1
5   2              3
6   1              1
7   3              2

результат:

CategoryNAme     Count
Category1          7
Category1.1        5
Category2.1        4
Category1.2        1
Category3.1        2

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

заранее спасибо за ответ

2 ответов


ваши таблицы и примеры данных

create table #Category(Id int identity(1,1),Name Varchar(255),parentId int)
INSERT INTO #Category(Name,parentId) values
('Category1',null),('Category1.1',1),('Category2.1',2),
('Category1.2',1),('Category3.1',3)

create table #CategoryItem(Id int identity(1,1),categoryId int,itemId int)
INSERT INTO #CategoryItem(categoryId,itemId) values
(5,1),(4,2),(5,2),(3,1),(2,3),(1,1),(3,2)

create table #Item(Id int identity(1,1),Name varchar(255))
INSERT INTO #Item(Name) values('item1'),('item2'),('item3')

проверка для всех детей родителя по Рекурсивные Выражения Таблицы Commom

;WITH CategorySearch(ID, parentId) AS
(
SELECT ID, ID AS ParentId FROM #Category
UNION ALL
SELECT CT.Id,CS.parentId  FROM #Category CT
INNER JOIN CategorySearch CS ON CT.ParentId = CS.ID
)
select * from CategorySearch order by 1,2

вывод: все дочерние записи против родителей

ID  parentId
1   1
2   1
3   1
4   1
5   1
2   2
3   2
5   2
3   3
5   3
4   4
5   5

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

;WITH CategorySearch(ID, parentId) AS
(
SELECT ID, ID AS ParentId FROM #Category
UNION ALL
SELECT CT.Id,CS.parentId  FROM #Category CT
INNER JOIN CategorySearch CS ON CT.ParentId = CS.ID
)
SELECT CA.Name AS CategoryName,count(itemId) CountItem
FROM #Category CA 
INNER JOIN CategorySearch CS ON CS.ParentId = CA.id
INNER JOIN #CategoryItem MI ON MI.CategoryId =CS.ID
GROUP BY CA.Name

выход:

CategoryName    CountItem
Category1           7
Category1.1         5
Category1.2         1
Category2.1         4
Category3.1         2

С помощью CTE (common table expression) с рекурсией вы можете достичь того, что ищете.

дополнительные сведения о рекурсивных CTEs см. в справке Microsoft:CTE MS SQL 2008+

таким образом, вы можете найти полный пример с вашими образцами данных:

-- tables definition 
SELECT 1 as id, 'cat1' as [name],NULL as id_parent
into cat
union
select 2, 'cat1.1', 1
union
select 3, 'cat2.1', 2
union
select 4, 'cat1.2', 1
union
select 5, 'cat3.1', 3

select 1 as id , 5 as id_cat, 1 as id_item
iNTO item
UNION
select 2, 4, 2
UNION
select 3, 5, 2
UNION
select 4, 3, 1
UNION
select 5, 2, 3
UNION
select 6, 1, 1
UNION
select 7, 3, 2

-- CTE to get desired result
with childs
as
(
    select c.id, c.id_parent
    from cat c 
    UNION ALL
    select s.id, p.id_parent
    from cat s JOIN childs p
        ON (s.id_parent=p.id)
),
category_count
AS
(
    SELECT c.id, c.name, count(i.id) as items
    from cat c left outer join item i
    on (c.id=i.id_cat)
    GROUP BY c.id,c.name
),
pairs
AS
(
    SELECT id, ISNULL(id_parent,id) as id_parent
    FROM childs 
)

select p.id_parent, n.name, sum(items)
from pairs p JOIN category_count cc
    ON (p.id=cc.id)
    join cat n ON (p.id_parent=n.id)
GROUP by p.id_parent ,n.name
ORDER by 1;