最近用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
文章到这里就结束了。若是文章对你有帮助,能够点个"推荐",也能够"关注"我,得到更多丰富的知识。若文中有什么不对或者不严谨的地方,请指正。