彙總函數與GROUP BY HAVING SUM()、COUNT()、AVG()、MAX()、MIN()、WITH ROLLUP、WITH CUBE
通常GROUP BY會搭配彙總函數使用,而常用的彙總函數有以下五個
SUM()、COUNT()、AVG()、MAX()、MIN()
先準備範例資料表,price為單價、amount為數量
CREATE TABLE [ORDER] ( no INT NOT NULL, name [NVARCHAR](10) NOT NULL, year SMALLINT NOT NULL, quarter TINYINT NOT NULL, item [NVARCHAR](10) NOT NULL, price [INT] NOT NULL, amount [INT] NOT NULL, PRIMARY KEY (no) ) go INSERT [ORDER] VALUES (1,'Bill',2005,1,'CPU',3000,1); INSERT [ORDER] VALUES (2,'Bill',2005,2,'RAM',2000,4); INSERT [ORDER] VALUES (3,'Bill',2006,4,'MB',2000,1); INSERT [ORDER] VALUES (4,'Mary',2010,1,'SSD',2500,5); INSERT [ORDER] VALUES (5,'Mary',2010,1,'LCD',6700,1); INSERT [ORDER] VALUES (6,'Bryan',2015,1,'KB',500,2); INSERT [ORDER] VALUES (7,'Bryan',2015,3,'ROUTER',1500,2); INSERT [ORDER] VALUES (8,'Tim',2016,1,'CPU',12000,1); INSERT [ORDER] VALUES (9,'Tim',2016,4,'MB',5000,1);
一、SUM()
以name為群組,計算每個人共花了多少錢
SELECT no,name,year,quarter,item,price,amount FROM [ORDER] SELECT name,Sum(price * amount) AS total FROM [ORDER] GROUP BY name
二、COUNT()
以name為群組,計算每個人共買了幾類東西
SELECT name,Count(item) FROM [ORDER] GROUP BY name SELECT name,Count(*) FROM [ORDER] GROUP BY name
使用COUNT()函數時,在小括弧內可以填入item、no或是資料表內的任何一個屬性,
甚至是填入萬用字元「*」,但除非有N個欄位裡有很多null值,
而不知道要COUNT那一個欄位,否則建議不要使用「*」,
因為將會造成效率降低。至於使用其他屬性時,也必須要注意到該屬性值有沒有可能是null,
因為COUNT()函數是依據該屬性值非null時再累加一,因此有null值時,
有可能會造成計數不正確,在此建議可以填入主要屬性,
因為主要鍵的屬性值不可能為null值。
三、AVG()
以name為群組,計算每個人平均於每件商品花了多少錢
SELECT no,name,year,quarter,item,price,amount FROM [ORDER] SELECT name,Avg(price * amount) FROM [ORDER] GROUP BY name
四、MAX()
以name為群組,列出每個人花最多錢於哪類商品而花了多少錢。
先直接看目標答案
SELECT no,name,year,quarter,item,price,amount FROM [ORDER]
如果使用GROUP BY name配合MAX(price*amount)時,
select_list除了被GROUP BY的欄位外,只能使用彙總函數而已,
但我想一併列出該筆的購買時間、類別都因GROUP的限制而無法列出,
應該是MAX()等之類的彙總函數,就真的只有「彙總」功能而已,
彙總完成後的結果該筆記錄基本上就跟來源沒關係了。
因此決定使用TOP 1...ORDER BY...DESC來間接完成MAX()功能。
SELECT TOP 1 no,name,year,quarter,item,price,amount,( price * amount ) total INTO #table_bill_5 FROM [ORDER] WHERE name = 'Bill' ORDER BY ( price * amount ) DESC SELECT TOP 1 no,name,year,quarter,item,price,amount,( price * amount ) total INTO #table_mary_5 FROM [ORDER] WHERE name = 'Mary' ORDER BY ( price * amount ) DESC SELECT TOP 1 no,name,year,quarter,item,price,amount,( price * amount ) total INTO #table_bryan_5 FROM [ORDER] WHERE name = 'Bryan' ORDER BY ( price * amount ) DESC SELECT TOP 1 no,name,year,quarter,item,price,amount,( price * amount ) total INTO #table_tim_5 FROM [ORDER] WHERE name = 'Tim' ORDER BY ( price * amount ) DESC SELECT no,name,year,quarter,item,price,amount,total FROM #table_bill_5 UNION SELECT no,name,year,quarter,item,price,amount,total FROM #table_mary_5 UNION SELECT no,name,year,quarter,item,price,amount,total FROM #table_bryan_5 UNION SELECT no,name,year,quarter,item,price,amount,total FROM #table_tim_5
上例使用了暫存表又使用了UNION,還假設已知只有四個人購買,
整個Query又臭又長的...,相信還有改善的空間。
這個問題已牽扯到流程控制或子查詢範圍了,於介紹MAX()範例已足夠。
五、MIN()
請參考MAX()範例
六、HAVING
如果只想列出某個人之所有花費,則在GROUP BY之後使用HAVING,
做彙總後條件篩選。
SELECT no,name,year,quarter,item,price,amount FROM [ORDER] SELECT name,Sum(price * amount) AS total FROM [ORDER] GROUP BY name HAVING name = 'Bill'
七、WITH ROLLUP
此功能可以列出個人花費與所有人花費,類似EXCEL的統計表。
注意,WITH ROLLUP是對此人的所有年度(name,year)組合做加總然後再加上一個總合計。
SELECT no,name,year,quarter,item,price,amount FROM [ORDER] SELECT name,year,Sum(price * amount) total FROM [ORDER] GROUP BY name,year WITH ROLLUP
八、WITH CUBE
與WITH ROLLUP不同的是,擁有「對此人的所有年度(name,year)組合做加總然後再加上一個總合計」之外,
再細列出個人個年度花費(如下圖綠色線條)
SELECT no,name,year,quarter,item,price,amount FROM [ORDER] SELECT name,year,Sum(price*amount) total FROM [ORDER] GROUP BY name,year WITH CUBE