新闻资讯

新闻资讯 媒体报道

MySQL学习,详解分组查询(二)

编辑:016     时间:2021-10-14

分组后排序

需求:获取每个⽤户最⼤⾦额,然后按照最⼤⾦额倒序,输出:⽤户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,ER

ROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |

+---------------------------------------------------------------------

----------------------------------------------------------------------

+

1 row in set (0.00 sec)sqlmode中包含了`ONLYFULLGROUPBY`,这个表⽰select后⾯的列必须符合上⾯的说的2点

规范。

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

修改mysql中的my.ini⽂件:

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DI

VISION_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)

对⽐⼀下,userid=1001、price=88.88是第3条数据,即theyear是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,顺序不

能搞错了,否则报错。

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐