玩转Mysql系列 - 第9篇:分组查询详解(group by & having)(1)

HAVING count(id)>=2;

±---------±-------------+

| 用户id   | 下单数量     |

±---------±-------------+

|     1001 |            2 |

|     1002 |            3 |

±---------±-------------+

2 rows in set (0.00 sec)

方式2:

mysql> SELECT

user_id 用户id, count(id) 下单数量

FROM

t_order t

WHERE

t.the_year = 2018

GROUP BY user_id

HAVING 下单数量>=2;

±---------±-------------+

| 用户id   | 下单数量     |

±---------±-------------+

|     1001 |            2 |

|     1002 |            3 |

±---------±-------------+

2 rows in set (0.00 sec)

where和having的区别

where是在分组(聚合)前对记录进行筛选,而having是在分组结束后的结果里筛选,最后返回整个sql的查询结果。

可以把having理解为两级查询,即含having的查询操作先获得不含having子句时的sql查询结果表,然后在这个结果表上使用having条件筛选出符合的记录,最后返回这些记录,因此,having后是可以跟聚合函数的,并且这个聚集函数不必与select后面的聚集函数相同。

分组后排序

需求:获取每个用户最大金额,然后按照最大金额倒序,输出:用户id,最大金额,如下:

mysql> SELECT

user_id 用户id, max(price) 最大金额

FROM

t_order t

GROUP BY user_id

ORDER BY 最大金额 desc;

±---------±-------------+

| 用户id   | 最大金额     |

±---------±-------------+

|     1001 |        88.88 |

|     1003 |        66.66 |

|     1002 |        44.44 |

±---------±-------------+

3 rows in set (0.00 sec)

where & group by & having & order by & limit 一起协作

where、group by、having、order by、limit这些关键字一起使用时,先后顺序有明确的限制,语法如下:

select 列 from

表名

where [查询条件]

group by [分组表达式]

having [分组过滤条件]

order by [排序条件]

limit [offset,] count;

注意:

写法上面必须按照上面的顺序来写。

示例:

**需求:**查询出2018年,下单数量大于等于2的,按照下单数量降序排序,最后只输出第1条记录,显示:用户id,下单数量,如下:

mysql> SELECT

user_id 用户id, COUNT(id) 下单数量

FROM

t_order t

WHERE

t.the_year = 2018

GROUP BY user_id

HAVING count(id)>=2

ORDER BY 下单数量 DESC

LIMIT 1;

±---------±-------------+

| 用户id   | 下单数量     |

±---------±-------------+

|     1002 |            3 |

±---------±-------------+

1 row in set (0.00 sec)

mysql分组中的坑

本文开头有介绍,分组中select后面的列只能有2种:

  1. 出现在group by后面的列

  2. 使用聚合函数的列

oracle、sqlserver、db2中也是按照这种规范来的。

文中使用的是5.7版本,默认是按照这种规范来的。

mysql早期的一些版本,没有上面这些要求,select后面可以跟任何合法的列。

示例

需求:获取每个用户下单的最大金额及下单的年份,输出:用户id,最大金额,年份,写法如下:

mysql> select

user_id 用户id, max(price) 最大金额, the_year 年份

FROM t_order t

GROUP BY t.user_id;

ERROR 1055 (42000): Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘javacode2018.t.the_year’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

上面的sql报错了,原因因为the_year不符合上面说的2条规则(select后面的列必须出现在group by中或者使用聚合函数),而sql_mode限制了这种规则,我们看一下sql_mode的配置:

mysql> select @@sql_mode;

±------------------------------------------------------------------------------------------------------------------------------------------+

| @@sql_mode                                                                                                                                |

±------------------------------------------------------------------------------------------------------------------------------------------+

| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |

±------------------------------------------------------------------------------------------------------------------------------------------+

1 row in set (0.00 sec)

sql_mode中包含了ONLY_FULL_GROUP_BY,这个表示select后面的列必须符合上面的说的2点规范。

可以将ONLY_FULL_GROUP_BY去掉,select后面就可以加任意列了,我们来看一下效果。

修改mysql中的my.ini文件:

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

重启mysql,再次运行,效果如下:

mysql> select

user_id 用户id, max(price) 最大金额, the_year 年份

FROM t_order t

GROUP BY t.user_id;

±---------±-------------±-------+

| 用户id   | 最大金额     | 年份   |

±---------±-------------±-------+

|     1001 |        88.88 |   2017 |

|     1002 |        44.44 |   2018 |

|     1003 |        66.66 |   2018 |

±---------±-------------±-------+

3 rows in set (0.03 sec)

看一下上面的数据,第一条88.88的年份是2017年,我们再来看一下原始数据:

mysql> select * from t_order;

±—±--------±--------------±------±---------+

| id | user_id | user_name     | price | the_year |

±—±--------±--------------±------±---------+

|  1 |    1001 | 路人甲Java    | 11.11 |     2017 |

|  2 |    1001 | 路人甲Java    | 22.22 |     2018 |

|  3 |    1001 | 路人甲Java    | 88.88 |     2018 |

|  4 |    1002 | 刘德华        | 33.33 |     2018 |

|  5 |    1002 | 刘德华        | 12.22 |     2018 |

|  6 |    1002 | 刘德华        | 16.66 |     2018 |

|  7 |    1002 | 刘德华        | 44.44 |     2019 |

|  8 |    1003 | 张学友        | 55.55 |     2018 |

|  9 |    1003 | 张学友        | 66.66 |     2019 |

±—±--------±--------------±------±---------+

9 rows in set (0.00 sec)

对比一下,user_id=1001、price=88.88是第3条数据,即the_year是2018年,但是上面的分组结果是2017年,结果和我们预期的不一致,此时mysql对这种未按照规范来的列,乱序了,mysql取的是第一条。

正确的写法,提供两种,如下:

mysql> SELECT

user_id 用户id,

price 最大金额,

the_year 年份

FROM

t_order t1

WHERE

(t1.user_id , t1.price)

IN

(SELECT

t.user_id, MAX(t.price)

FROM

t_order t

GROUP BY t.user_id);

±---------±-------------±-------+

| 用户id   | 最大金额     | 年份   |

±---------±-------------±-------+

|     1001 |        88.88 |   2018 |

|     1002 |        44.44 |   2019 |

|     1003 |        66.66 |   2019 |

±---------±-------------±-------+

3 rows in set (0.00 sec)

mysql> SELECT

user_id 用户id,

price 最大金额,

the_year 年份

FROM

t_order t1,(SELECT

t.user_id uid, MAX(t.price) pc

FROM

t_order t

GROUP BY t.user_id) t2

WHERE

t1.user_id = t2.uid

AND  t1.price = t2.pc;

±---------±-------------±-------+

| 用户id   | 最大金额     | 年份   |

±---------±-------------±-------+

|     1001 |        88.88 |   2018 |

|     1002 |        44.44 |   2019 |

|     1003 |        66.66 |   2019 |

±---------±-------------±-------+

3 rows in set (0.00 sec)

上面第1种写法,比较少见,in中使用了多字段查询。

建议:在写分组查询的时候,最好按照标准的规范来写,select后面出现的列必须在group by中或者必须使用聚合函数。

总结

  1. 在写分组查询的时候,最好按照标准的规范来写,select后面出现的列必须在group by中或者必须使用聚合函数

  2. select语法顺序:select、from、where、group by、having、order by、limit,顺序不能搞错了,否则报错。

  3. in多列查询的使用,下去可以试试

Mysql系列目录

  1. 第1篇:mysql基础知识

  2. 第2篇:详解mysql数据类型(重点)

  3. 第3篇:管理员必备技能(必须掌握)

  4. 第4篇:DDL常见操作

  5. 第5篇:DML操作汇总(insert,update,delete)

  6. 第6篇:select查询基础篇

  7. 第7篇:玩转select条件查询,避免采坑

  8. 第8篇:详解排序和分页(order by & limit)

mysql系列大概有20多篇,喜欢的请关注一下,欢迎大家加我微信itsoku或者留言交流mysql相关技术!

java高并发系列全集

  1. 第1天:必须知道的几个概念

  2. 第2天:并发级别

  3. 第3天:有关并行的两个重要定律

  4. 第4天:JMM相关的一些概念

  5. 第5天:深入理解进程和线程

  6. 第6天:线程的基本操作

  7. 第7天:volatile与Java内存模型

  8. 第8天:线程组

  9. 第9天:用户线程和守护线程

  10. 第10天:线程安全和synchronized关键字

  11. 第11天:线程中断的几种方式

  12. 第12天JUC:ReentrantLock重入锁

  13. 第13天:JUC中的Condition对象

  14. 第14天:JUC中的LockSupport工具类,必备技能

  15. 第15天:JUC中的Semaphore(信号量)

  16. 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能

  17. 第17天:JUC中的循环栅栏CyclicBarrier的6种使用场景

  18. 第18天:JAVA线程池,这一篇就够了

  19. 第19天:JUC中的Executor框架详解1

  20. 第20天:JUC中的Executor框架详解2

  21. 第21天:java中的CAS,你需要知道的东西

  22. 第22天:JUC底层工具类Unsafe,高手必须要了解

  23. 第23天:JUC中原子类,一篇就够了

  24. 第24天:ThreadLocal、InheritableThreadLocal(通俗易懂)

总结

大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。

麻烦帮忙转发一下这篇文章+关注我

就这一次!拼多多内部架构师培训Kafka源码笔记(现已绝版)

信号量)]( )**

  1. 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能

  2. 第17天:JUC中的循环栅栏CyclicBarrier的6种使用场景

  3. 第18天:JAVA线程池,这一篇就够了

  4. 第19天:JUC中的Executor框架详解1

  5. 第20天:JUC中的Executor框架详解2

  6. 第21天:java中的CAS,你需要知道的东西

  7. 第22天:JUC底层工具类Unsafe,高手必须要了解

  8. 第23天:JUC中原子类,一篇就够了

  9. 第24天:ThreadLocal、InheritableThreadLocal(通俗易懂)

总结

大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。

麻烦帮忙转发一下这篇文章+关注我

[外链图片转存中…(img-ssZ8kdKB-1714759581208)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值