最近用MySQL做統計的需求比較多,這裏整理一些常用的場景方便後期查閱,同時也是拋磚引玉的過程。其中包括普通的分組統計,連續的每日統計,區間範圍統計。
普通分組統計
場景一:根據訂單狀態統計訂單數量。
一個很常見,也很簡單的統計需求。其中狀態字段是訂單實體的一個屬性。
@Query("SELECT status, COUNT(id) FROM Order GROUP BY status") fun summaryOrderByStatus(): Array<Array<String>>?
場景二:根據訂單中商品類目統計訂單數量和金額。
比場景一稍微麻煩了一點,商品字段是訂單實體的一個屬性,而類目字段纔是商品實體的一個屬性。參考代碼:(Kotlin語法)
@Query("SELECT commodity.category, COUNT(id), SUM(finalPrice) FROM Order GROUP BY commodity.category") fun summaryOrderByCommodityCategory(): Array<Array<String>>?
小結:
- 分組統計少不了GROUP BY語句,如果需要加查詢條件,請在其前面添加 WHERE 語句。
- 統計數量用COUNT,統計總和用SUM函數,有GROUP BY的地方,少不了這些聚合函數。
- 統計返回的結果是字符串類型的二維數組。
- 以內嵌屬性分組,如果是SpringDataJpa框架,則可以直接通過”實體類.屬性名”的方式。
每日統計
在做每日,每週,每月統計時,遇到返回日期不是連續的情況。
原因是數據庫中沒有值,而我們理想狀態應該是:如果沒有值則默認爲零,使其數據是連續的日期。
場景三:統計結果日期可能不連續
如果數據庫中某個時間段沒有值,那統計出來的結果會缺這段時間。參考代碼:(sql語句)
-- 統計每日 SELECT DATE_FORMAT(create_date,'%Y-%m-%d') as days, COUNT(id) count FROM order GROUP BY days; -- 統計每週 SELECT DATE_FORMAT(create_date,'%Y-%u') as weeks, COUNT(id) count FROM order GROUP BY weeks; -- 統計每月 SELECT DATE_FORMAT(create_date,'%Y-%m') as months, COUNT(id) count FROM order GROUP BY months;
場景四:統計結果日期連續
要讓日期連續,又要代碼優雅。說實話,困擾了我很久,一直沒有找到很好的解決方法,雖然目前這個方法很挫。但可以解決問題。畢竟抓到老鼠的都是好貓。如果各位有好的建議,望賜教!
解決思路:
- 第一步:創建一張date_summary輔助表,字段只需要有date和count(默認值爲零)。
- 第二步:先向date_summary表插入10年內的數據。
- 第三步:通過UNION ALL 聯合查詢,將空缺的日期補上。
- 第二步參考代碼(Kotlin語法)
第二步參考代碼(Kotlin語法)
val startDate = Calendar.getInstance() startDate.set(2018, 6, 1) val startTIme = startDate.timeInMillis val endDate = Calendar.getInstance() endDate.set(2028, 11, 30) val endTime = endDate.timeInMillis val oneDay = 1000 * 60 * 60 * 24L var time = startTIme val dates: MutableList<DateSummary> = arrayListOf() while (time<=endTime) { val dateSummary = DateSummary() dateSummary.date = SimpleDateFormat("yyyy-MM-dd").format(Date(time)) dateSummary.count = 0 dates.add(dateSummary) time += oneDay } dateSummaryRepository.saveAll(dates)
第三步統計每日的SQL語句
SELECT summary.oneDay, summary.count FROM ( SELECT DATE_FORMAT( created_date, '%Y-%m-%d' ) oneDay, COUNT(id) count FROM service_order WHERE created_date BETWEEN "2018-06-01" and "2018-08-01" GROUP BY oneDay UNION ALL ( SELECT DATE_FORMAT( date, '%Y-%m-%d' ) templateDay, count FROM date_summary WHERE date BETWEEN "2018-06-01" and "2018-08-01" GROUP BY templateDay ) ) summary GROUP BY summary.oneDay ORDER BY summary.oneDay ASC
小結:
- MySQL的DATE_FORMAT(date,format) 函數用於以不同的格式顯示日期/時間數據,文章後面會詳細介紹
- MySQL的UNION 操作符用於合併兩個或多個SELECT語句的結果集,文章後面會詳細介紹
區間範圍統計
這是一個較爲常見的需求,比如按照年齡段統計人員分佈情況,甚至要求分別統計男女人數分佈情況。
場景五:根據小區年齡段統計人數
只根據年齡範圍統計,沒有其他限制條件,使用SUM只需要加一。
SELECT INTERVAL(age,10,20,30,40,50,60,70,80,90) AS ageRatio, SUM(1) AS count FROM user GROUP BY ageRatio
場景六:根據小區年齡段統計男女人數
在場景五的基礎上多了一個區分性別,用流程控制函數來設置SUM加一的情況。
SELECT INTERVAL(age,10,20,30,40,50,60,70,80,90) AS ageRatio, SUM(CASE WHEN sex=1 THEN 1 ELSE 0 END) AS male, SUM(CASE WHEN sex=0 THEN 1 ELSE 0 END) AS female FROM user GROUP BY ageRatio
小結:
- 通過區間統計需要使用MySQL的INTERVAL函數,第一個參數是需要比較的字段,後面是比較的區間,值必須從小到大
- 區間統計的結果也是二維數組,注意返回的結果可能不是連續的(這裏的不連續可以用代碼解決,畢竟區間數量較少)。第一個參數返回的是區間的下標,從0開始。
- 當age的值在區間範圍內就SUM加一,也可以通過流程控制函數(CASE WHEN THEN ELSE END)來判斷是加一還是加零
MySQL知識點
知道現在都是快餐文化,大家都很忙,很少有時間去揣摩各語法的特點。所以先把常用的場景寫在前面,語法知識寫在後面。
- GROUP BY 分組
- 分組一般與聚合函數一起使用如SUM,COUNT等
- GROUP BY 在WHERE 語句之後
- DATE_FORMAT 時間格式化
- 用來修改時間的格式
- 語法格式: DATE_FORMAT(date,format) date必須是合格的時間參數,format是輸出時間格式
- 常見的format格式有:
- %Y: 4位數的年,
- %y: 2位數的年,
- %m: 2位數的月(00~12),
- %M: 英文單詞的月,
- %d: 2位數的日(00~31),
- %u: 周,星期一是一週的第一條,
- 更多可以訪問w3school
- UNION 聯合結果
- UNION可以合併、聯合,將多次查詢結果合併成一個結果,通過查詢結果合併解決了統計不連續的情況。
- 多條查詢語句的列數必須一致,各列的順序最好一致。場景四中,兩條sql都只查詢了date和count,且順序保持一致。
- union 去重,union all包含重複項
- INTERVAL 比較間距
- INTERVAL()函數是比較列表(N, arg1, arg2, arg3…argN)中的N值。
- INTERVAL()函數如果N<arg1則返回0,如果N<arg2則返回1,如果N<arg3則返回2,如果N爲NULL,它將返回-1。
- 列表值必須是arg1 < arg2 < arg3 的形式才能正常工作。
- 流程控制函數
- case when then else end 是流程控制函數中的一種,還有一種是if函數
- 使用語法:
case when 條件1 then 值1 when 條件2 then 值2 ... else 值n end