最近用MySQL作統計的需求比較多,這裏整理一些經常使用的場景方便後期查閱,同時也是拋磚引玉的過程。其中包括普通的分組統計,連續的每日統計,區間範圍統計。java
技術:MySQL, SpringDataJpa, Kotlin
說明:文章前半部分是場景分析,後半部分是語法分析
要點:GROUP BY, UNION, DATE_FORMAT, 流程控制函數mysql
一個很常見,也很簡單的統計需求。其中狀態字段是訂單實體的一個屬性。參考代碼:(Kotlin語法)sql
@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 HOUR(create_date) hour,count(*) count FROM order WHERE create_date like '2019-06-24%' GROUP BY hour ORDER BY hour; -- 統計每日 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;
要讓日期連續,又要代碼優雅。說實話,困擾了我好久,一直沒有找到很好的解決方法,雖然目前這個方法很挫。但能夠解決問題。畢竟抓到老鼠的都是好貓。若是各位有好的建議,望賜教!this
解決思路:
第一步:建立一張date_summary輔助表,字段只須要有date和count(默認值爲零)。
第二步:先向date_summary表插入10年內的數據。
第三步:經過UNION ALL 聯合查詢,將空缺的日期補上。code
第二步參考代碼(Kotlin語法)orm
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
----------------------------------------------補充修改--------------------------------------------------------
上面一段sql在MySQL5.7以前是能夠正常運行的,當Mysql5.7以後會提示錯誤
xxx which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
緣由是MySQL5.7 默認爲ONLY_FULL_GROUP_BY: 大概意思就是:用了group by field,field就要有聚合操做。
解決方法:嚴格按照MySQL的標準來修改sql語句,極力反對經過修改配置取消MySQL的ONLY_FULL_GROUP_BY校驗的方法。
SELECT summary.oneDay, summary.count FROM ( SELECT DATE_FORMAT( create_time, '%Y-%m-%d' ) oneDay, COUNT( id ) count FROM alarm_record WHERE alarm_object_type = '0' AND create_time BETWEEN '2019-05-01 00:00:00' AND '2019-05-30 00:00:00' GROUP BY oneDay UNION ALL ( SELECT DATE_FORMAT( date, '%Y-%m-%d' ), count FROM date_summary WHERE date BETWEEN DATE_FORMAT( '2019-05-01 00:00:00', '%Y-%m-%d' ) AND DATE_FORMAT( '2019-05-30 00:00:00', '%Y-%m-%d' ) ) ) summary 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)來判斷是加一仍是加零
知道如今都是快餐文化,你們都很忙,不多有時間去揣摩各語法的特色。因此先把經常使用的場景寫在前面,語法知識寫在後面。
一)、分組通常與聚合函數一塊兒使用如SUM,COUNT等
二)、GROUP BY 在WHERE 語句以後
一)、用來修改時間的格式
二)、語法格式: DATE_FORMAT(date,format) date必須是合格的時間參數,format是輸出時間格式
三)、常見的format格式有:
一)、UNION能夠合併、聯合,將屢次查詢結果合併成一個結果,經過查詢結果合併解決了統計不連續的狀況。
二)、多條查詢語句的列數必須一致,各列的順序最好一致。場景四中,兩條sql都只查詢了date和count,且順序保持一致。
三)、union 去重,union all包含重複項
一)、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
文章到這裏就結束了。若是文章對你有幫助,能夠點個"推薦",也能夠"關注"我,得到更多豐富的知識。若文中有什麼不對或者不嚴謹的地方,請指正。