2011-06-07 78 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