我正在尝试在典型的餐馆类型数据库上提供报告功能。我将在下面描述问题的具体细节,但简而言之,我需要能够检索与层次结构自连“类别”表相关的项目的聚合数据(总和和计数)。我知道这很冗长,可能会让人感到困惑,所以我会尝试用一个例子来传达细节。我有四个特定于此问题的表:
Categories
Id Name ParentId
1 Food NULL
2 Drinks NULL
3 Beer 2
4 Draft Beer 3
5 Bottle Beer 4
6 Pizza 1
8 Sandwiches 1
ParentId是FK回到分类
MenuItems
Id Name CategoryId
1 6" Sausage 6
2 French Dip 8
3 Dogfish 60 IPA 4
4 Dogfish 60 IPA 5
5 Bud Light 5
Orders
Id Total DateReceived DateCompleted
1 12.00 1/1/1970 1:00 1/1/1970 1:05
2 11.50 1/1/1970 1:08 1/1/1970 1:18
OrderItems
Id Price OrderId MenuItemId
1 9.00 1 1
2 3.00 1 5
3 3.50 2 3
4 8.00 2 2
此报告功能的目标是获取categoryId并返回给定时间段内的总和(或计数)。如果传入的类别是叶子类别(例如瓶啤酒),我在计算此总数时没有问题。然而,如果它在层面上更高,例如“食物”,我无法弄清楚如何编写查询以便总结所有子类别。
我正在使用SQL Server 2008r2。我尝试使用WITH common_table_expression查询,但是我收到了关于“递归cte的递归部分”中不允许的聚合函数的错误。
我今天拥有的SQL看起来像这样:
SELECT SUM(oi.Price) as Total, DAY(o.Completed)
FROM Orders o
JOIN OrderItems oi on oi.orderId = o.id
JOIN MenuItems mi on oi.MenuItemId = mi.id
JOIN Categories c on mi.CategoryId = c.id
WHERE o.Completed is not null
AND c.Id = @categoryId
AND o.Completed between @startDate and @endDate
GROUP BY DAY(o.completed)
同样,如果@categoryId是一个叶子类别,这个查询给了我正是我正在寻找的东西。如果不是,则不返回任何内容。我希望能够提供@category = 2(饮料)并在“饮料”类别层次结构中的任何位置获取所有项目的总和。
谢谢!
答案 0 :(得分:2)
Tobyb,
试图用CTE递归方法解决这个问题,你实际上非常接近标记。递归在最好的时候是混乱的,但是对于这类问题是最好的解决方案
我已经复制了您在问题中指定的模式,并提出了以下解决方案。
我测试了它并产生了以下输出......我假设这是你想看到的......
CategoryName CategoryId TotalSaleAmount
Food 1 17.00
Drinks 2 6.50
Beer 3 6.50
Draft Beer 4 3.50
Bottle Beer 5 3.00
Pizza 6 9.00
Sandwiches 8 8.00
显然17美元的食物是由披萨和三明治组成的。同样,6.50美元的饮料是由6.50美元的啤酒制成的,而后者又分别来自草案和瓶装啤酒的3.50美元和3.00美元......等等......这与层次结构相匹配类别。
代码如下,这应该适用于您的架构。如果您希望限制输出,请在分组前添加一个where子句。
WITH CategorySearch(ParentId, Id) AS
(
SELECT ID AS ParentId, ID
FROM Categories
WHERE ID NOT IN (SELECT ParentId FROM Categories
WHERE ParentId IS NOT NULL)
UNION ALL
SELECT Categories.ParentId, CS.Id
FROM Categories Categories INNER JOIN CategorySearch CS
ON Categories.Id = CS.ParentId
WHERE Categories.ParentId IS NOT NULL
)
SELECT CA.Name AS CategoryName, CS.ParentId AS CategoryID, SUM(OI.Price) AS TotalSaleAmount
FROM Categories CA INNER JOIN CategorySearch CS
ON CS.ParentId = CA.ID
INNER JOIN MenuItems MI
ON MI.CategoryId = CS.Id
INNER JOIN OrderItems OI
ON OI.MenuItemId = MI.ID
INNER JOIN Orders O
ON OI.OrderId = O.Id
GROUP BY CA.Name, CS.ParentId
ORDER BY CS.ParentId
答案 1 :(得分:0)
您是否尝试过使用递归CTE来确定感兴趣的类别ID,然后在进行聚合时加入这些结果?我想是这样的事情:
WITH CatR AS
(
SELECT Id FROM Categories WHERE Id = @CategoryId
UNION ALL
SELECT Categories.Id
FROM Cat
INNER JOIN Categories ON Categories.ParentId = Cat.Id
)
SELECT SUM(Price)
FROM Orders
INNER JOIN OrderItems ON OrderItems.OrderId = Orders.Id
INNER JOIN MenuItems ON MenuItems.Id = OrderItems.MenuItemId
INNER JOIN CatR ON CatR.Id = MenuItems.CategoryId
WHERE Orders.DateCompleted BETWEEN @StartDate AND @EndDate
答案 2 :(得分:-1)
我会更改数据库。问题是你在同一个表中有两个不同类别的项目。
将Food and Drink放入CategoryType表中。 将其余部分留在Category表中。 加入他们。
或者,要避免使用其他表,请在“类别”表中添加类别类型字段。类型可以是食物或饮料。您可以使用“食物”和“饮料”或“F”和“D”。纯粹主义者可能不喜欢这样,但优点是它更简单,运行速度更快。