2011-06-07 68 views
3

我试图在典型的餐厅类型数据库上提供报告功能。我在下面描述了这个问题的具体情况,但简而言之,我需要能够检索与自动加入“类别”表的项目有关的汇总数据(总和和计数)。我知道这很罗嗦,可能会让人困惑,所以我会试着用一个例子来传递细节。我必须具体到这个问题的四个表:使用SQL Server 2008r2中的自连接表来汇总数据

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并返回之和(或计数)在一段时间。如果传入的类别是一个叶子类别(例如瓶子啤酒),我没有计算这个总数的问题。但是,如果它的层次更高,例如“Food”,我就无法弄清楚如何编写查询,以便总结所有的子类别。

我正在使用SQL Server 2008r2。我尝试使用WITH common_table_expression查询,但我得到了关于“recursive 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(Drinks)并获取“Drinks”类别层次结构中任何位置的所有项目的总和。

谢谢!

回答

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美元组成......等等。
这匹配类别的层次结构。

代码如下,这应该适用于您的模式。如果你想限制输出,在group by之前加上一个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 
+0

太棒了!非常感谢。正是我在找什么。 – tobyb 2011-06-07 06:00:29

-1

我会改变数据库。问题是你在同一个表中有两个不同类的项目。

将Food and Drink放在CategoryType表中。 将剩下的部分留在类别表中。 加入他们。

或者,要避免另一个表,请向类别表中添加一个类别类型字段。类型可以是食物或饮料。您可以使用“食物”和“饮料”或“F”和“D”。纯粹主义者可能不喜欢这样,但优点是它更简单,运行速度更快。

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 
+0

感谢您的回复!我正在接近这一点,但不能完全正确。我会给它一个镜头。 – tobyb 2011-06-07 06:01:34