MySQL全面瓦解10:分组查询和聚合函数

概述

相信我们经常会遇到这样的场景:想要了解双十一天猫购买化妆品的人员中平均消费额度是多少(这可能有利于对商品价格区间的定位);或者不同年龄段的化妆品消费占比是多少(这可能有助于对商品备货量的预估)。

这个时候就要用到分组查询,分组查询的目的是为了把数据分成多个逻辑组(购买化妆品的人员是一个组,不同年龄段购买化妆品的人员也是组),并对每个组进行聚合计算的过程:。

分组查询的语法格式如下:

1 select cname,group_fun,... from tname [where condition]
2 group by group_expression [having group_condition]; 

说明一下:

1、group_fun 代表聚合函数,是指对分组的数据进行聚合计算的函数。

2、group_expression 代表分组表达式,允许多个,多个之间使用逗号隔开。

3、group_condition 分组之后,再对分组后的数据进行条件过滤的过程。

4、分组语法中,select后面出现的字段 要么是group by后面的字段,要么是聚合函数的列,其他类型会报异常,我们下面的内容中会详细说明。 

说分组之前,先来看看聚合函数,聚合函数是分组查询语法格式中重要的一部分。我们经常需要汇总数据而不用把它们实际检索出来,所以MySQL供了专门的函数。使用这些函数,可用于计算我们需要的数据,以便分析和生成报表。

聚合函数

聚合函数有以下几种。 

函数 说明
AVG() 返回指定字段的平均值
COUNT() 返回查询结果行数
MAX() 返回指定字段的最大值 
MIN() 返回指定字段的最小值
SUM() 返回指定字段的求和值

AVG()函数

AVG()通过对表中行数计数并计算特定列值之和,求得该列的平均值。 AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。

下面示例返回用户表中用户的平均年龄:

 1 mysql> select * from user2;
 2 +----+--------+------+----------+-----+
 3 | id | name   | age  | address  | sex |
 4 +----+--------+------+----------+-----+
 5 |  1 | brand  |   21 | fuzhou   |   1 |
 6 |  2 | helen  |   20 | quanzhou |   0 |
 7 |  3 | sol    |   21 | xiamen   |    8 |  4 | weng   |   33 | guizhou  |    9 |  5 | selina |   25 | NULL     |   10 |  6 | anny   |   23 | shanghai |   11 |  7 | annd   |   24 | shanghai |   12 |  8 | sunny  | NULL | guizhou  |   13 +----+--------+------+----------+-----+
14 8 rows in set
15 
16 mysql> select avg(age) 17 +----------+
18 | avg(age) |
19 +----------+
20 | 23.8571  |
21 +----------+
22 1 row set 

注意点:

1、AVG()只能用来确定特定数值列的平均值 。
2、AVG()函数忽略列值为NULL的行,所以上图中age值累加之后是除以7,而不是除以8。 

 

COUNT()函数

COUNT()函数进行计数。 可以用COUNT()确定表中符合条件的行的数目。

count 有 count(*)、count(具体字段)、count(常量) 三种方式来体现 下面 演示了count(*) 和 count(cname)的用法。

8 | sunny  | NULL | guizhou  |   select count(*) from user2 where sex=0;
18 | count(*) |
20 |        5 |
23 
24 mysql> count(age) 25 +------------+
26 | count(age) |
27 +------------+
28 |          4 |
29 +------------+
30 set 

可以看到,都是取出女生的用户数量,count(*) 比 count(age) 多一个,那是因为age中包含null值。

所以:如果指定列名,则指定列的值为空的行被COUNT()函数忽略,但如果COUNT()函数中用的是星号( *),则不忽略。 

关于count 可以看我写的另一篇,详细分析了几种count的使用和性能比较: SELECT COUNT 小结

MAX()和MIN()函数

MAX()返回指定列中的最大值,MIN()返回指定列中的最小值

select max(age),min(age) 17 +----------+----------+
18 | max(age) | min(age) |
19 +----------+----------+
20 |       33 |       20 |
21 +----------+----------+
set 

 注意:同样的,MAX()、MIN()函数忽略列值为NULL的行。

SUM函数

SUM()用来返回指定列值的和(总计) ,下面返回了所有年龄的总和,同样的,忽略了null的值

select sum(age) 18 | sum(age) |
167      |
set

分组查询

数据准备,假设我们有一个订货单表如下(记载用户的订单金额和下单时间):

 t_order;
 2 +---------+-----+-------+--------+---------------------+------+
 3 | orderid | uid | uname | amount | time                | year |
 4 +---------+-----+-------+--------+---------------------+------+
 5 |      20 |   1 | brand | 91.23  | 2018-08-20 17:22:21 | 2018 |
 6 |      21 |   87.54  | 2019-07-16 09:21:30 | 2019 |
 7 |      22 |   166.88 | 04-04 12:23:55 |  8 |      23 |   2 | helyn | 93.73  | 09-15 10:11:11 |  9 |      24 |   102.32 | 01-08 33:25 | 10 |      25 |   106.06 | 12-24 25:11 |      26 |   73.42  | 2020-03 16:23 | 2020 |
12 |      27 |   3 | sol   | 55.55  | 05 19:13 |      28 |   69.96  | 16 | 14 |      29 |   4 | weng  | 199.99 | 06-55:06 | 15 +---------+-----+-------+--------+---------------------+------+
10 rows set 

单字段分组

即对于某个字段进行分组,比如针对用户进行分组,输出他们的用户Id,订单数量和总额:

select uid,count(uid),sum(amount)  t_order group by uid;
 2 +-----+------------+-------------+
 3 | uid | count(uid) | sum(amount) |
 4 +-----+------------+-------------+
 5 |   1 |          3 | 345.65      |
 6 |   2 |          4 | 375.53      |
 7 |   3 |          2 | 125.51      |
 8 |   4 |          1 | 199.99      |
 9 +-----+------------+-------------+
10 4 rows set 

多字段分组

即对于多个字段进行分组,比如针对用户进行分组,再对他们不同年份的订单数据进行分组,输出订单数量和消费总额:

as nums,1)">as totalamount,year  t_order group by uid,year;
 2 +-----+------+-------------+------+
 3 | uid | nums | totalamount | year |
 4 +-----+------+-------------+------+
1 |    91.23       | 254.42      | 2 |    302.11      | 73.42       |  9 |   3 |    55.55       | 10 |   69.96       | 11 |   4 |    199.99      | 12 +-----+------+-------------+------+
13 7 rows set 

分组前的条件过滤:where

这个很简单,就是再分组(group by)之前通过where关键字进行条件过滤,取出我们需要的数据,假设我们只要列出2019年8月之后的数据,源数据只有6条合格的,有两条年份一样被分组的:

from t_order where time > '2019-08-01' group by uid,1)">199.79      | 10 +-----+------+-------------+------+
11 5 rows set 

分组后的条件过滤:having

有时候我们需要再分组之后再对数据进行过滤,这时候就需要使用having关键字进行数据过滤,再上述条件下,我们需要取出消费次数超过一次的数据:

1 mysql> ' group by uid,year having nums>1;
2 +-----+------+-------------+------+
3 | uid | nums | totalamount | year |
4 +-----+------+-------------+------+
5 |   6 +-----+------+-------------+------+
7 set 

这边需要注意区分where和having:

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

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

分组后的排序处理

order条件接在group by后面,也就是统计出每个用户的消费总额和消费次数后,对用户的消费总额进行降序排序的过程。

as totalamount  2 +-----+------+-------------+
 3 | uid | nums | totalamount |
 4 +-----+------+-------------+
 9 +-----+------+-------------+
11 
12 mysql>  t_order group by uid order by totalamount desc;
13 +-----+------+-------------+
14 | uid | nums | totalamount |
15 +-----+------+-------------+
16 |   17 |   18 |   19 |   20 +-----+------+-------------+
21 set 

分组后的limit 限制

limit限制关键字一般放在语句的最末尾,比如基于我们上面的搜索,我们再limit 1,只取出消费额最高的那条,其他跳过。

from t_order group by uid order by totalamount desc limit 1;
2 +-----+------+-------------+
3 | uid | nums | totalamount |
4 +-----+------+-------------+
6 +-----+------+-------------+
set 

关键字的执行顺序

我们看到上面那我们用了 where、group by、having、order by、limit这些关键字,如果一起使用,他们是有先后顺序,顺序错了会导致异常,语法格式如下:

select cname  tname
2 where [原表查询条件]
3 group by [分组表达式]
4 having [分组过滤条件]
5 order by [排序条件]
6 limit [offset,] count;

 

' group by uid having totalamount>100 order by totalamount desc limit 1273.21      |
set

 

总结

1、分组语法中,select后面出现的字段 要么是group by后面的字段,要么是聚合函数的列,其他类型会报异常:可以自己试试。

2、分组关键字的执行顺序:where、group by、having、order by、limit,顺序不能调换,否则会报异常:可以自己试试。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


navicat查看某个表的所有字段的详细信息 navicat设计表只能一次查看一个字段的备注信息,那怎么才能做到一次性查询表的信息呢?SELECT COLUMN_NAME,COLUMN_COMMENT,COLUMN_TYPE,COLUMN_KEY FROM information_schema.CO
文章浏览阅读4.3k次。转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52768613前言:数据库每天的数据不断增多,自动删除机制总体风险太大,想保留更多历史性的数据供查询,于是从小的hbase换到大的hbase上,势在必行。今天记录下这次数据仓库迁移。看下Agenda:彻底卸载MySQL安装MySQL_linux服务器进行数据迁移
文章浏览阅读488次。恢复步骤概要备份frm、ibd文件如果mysql版本发生变化,安装回原本的mysql版本创建和原本库名一致新库,字符集都要保持一样通过frm获取到原先的表结构,通过的得到的表结构创建一个和原先结构一样的空表。使用“ALTER TABLE DISCARD TABLESPACE;”命令卸载掉表空间将原先的ibd拷贝到mysql的仓库下添加用户权限 “chown . .ibd”,如果是操作和mysql的使用权限一致可以跳过通过“ALTER TABLE IMPORT TABLESPACE;”命令恢_alter table discard tablespace
文章浏览阅读225次。当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化:单表优化除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以下是没有太大问题的。而事实上很多时候MySQL单表的性能依然有不少优化空间,甚至能正常支撑千万级以上的数据量:字段尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNEDVARCHAR的长度只分配_开发项目 浏览记录表 过大怎么办
文章浏览阅读1.5k次。Mysql创建、删除用户MySql中添加用户,新建数据库,用户授权,删除用户,修改密码(注意每行后边都跟个;表示一个命令语句结束):1.新建用户登录MYSQL:@>mysql -u root -p@>密码创建用户:mysql> insert into mysql.user(Host,User,Password) values("localhost_删除mysql用户组
MySQL是一种开源的关系型数据库管理系统,被广泛应用于各类应用程序的开发中。对于MySQL中的字段,我们需要进行数据类型以及默认值的设置,这对于数据的存储和使用至关重要。其中,有一个非常重要的概念就是MySQL字段默认字符串。 CREATE TABLE `my_...
MySQL是一个流行的开源关系型数据库管理系统,广泛应用于Web应用程序开发、数据存储和管理。在使用MySQL时,正确设置字符集非常重要,以确保数据的正确性和可靠性。 在MySQL中,字符集表示为一系列字符和字母的集合。MySQL支持多种字符集,包括ASCII、UTF...
MySQL存储函数 n以内偶数 MySQL存储函数能够帮助用户简化操作,提高效率,常常被用于计算和处理数据。下面我们就来了解一下如何使用MySQL存储函数计算n以内的偶数。 定义存储函数 首先,我们需要定义一个MySQL存储函数,以计算n以内的偶数。下...
MySQL是一个流行的关系型数据库管理系统,基于客户机-服务器模式,可在各种操作系统上运行。 MySQL支持多种字符集,不同的字符集包括不同的字符,如字母、数字、符号等,并提供不同的排序规则,以满足不同语言环境的需求。 //查看MySQL支持的字符集与校对规...
在MySQL数据库中,我们有时需要对特定的字符串进行截取并进行分组统计。这种操作对于数据分析和报表制作有着重要的应用。下面我们将讲解一些基本的字符串截取和分组统计的方法。 首先,我们可以使用substring函数对字段中的字符串进行截取。假设我们有一张表stude...
MySQL提供了多种字符串的查找函数。下面我们就一一介绍。 1. LIKE函数 SELECT * FROM mytable WHERE mycolumn LIKE 'apple%'; 其中"apple%"表示以apple开头的字符串,%表示任意多个字符...
MySQL 是一种关系型数据库管理系统,广泛应用于各种不同规模和类型的应用程序中。在 MySQL 中,处理字符串数据是很常见的任务。有时候,我们需要在字符串的开头添加一定数量的 0 ,以达到一定的位数。比如,我们可能需要将一个数字转换为 4 位或 5 位的字符串,不足的...
MySQL是一种流行的关系型数据库管理系统,支持多种数据类型。以下是MySQL所支持的数据类型: 1. 数值型数据类型: - TINYINT 保存-128到127范围内的整数 - SMALLINT 保存-32768到32767范围内的整数 - MEDIU...
MySQL中存储Emoji表情字段类型 在现代互联网生态中,表情符号已经成为人们展示情感和思想的重要方式之一,因此将表情符号存储到数据库中是一个经常出现的问题。MySQL作为最流行的开源关系型数据库管理系统之一,也需要能够存储和管理这些表情符号的字段类型。 UT...
MySQL是一种关系型数据库管理系统。在MySQL数据库中,有多种不同的数据类型。而其中,最常见的数据类型之一就是字符串类型。在MySQL中,字符串类型的数据通常会被存储为TEXT或VARCHAR类型。 首先,让我们来看一下VARCHAR类型。VARCHAR是My...
MySQL字符串取整知识详解 MySQL是一种开源的关系型数据库管理系统,广泛应用于各个领域。在使用MySQL过程当中,我们经常需要对数据进行取整操作。本文将介绍如何使用MySQL字符串取整来处理数据取整问题。 什么是MySQL字符串取整? MySQL...
使用MySQL进行数据存储是现代应用程序开发中一个非常重要的组成部分。在MySQL中,数据存储的一个重要特点就是字符长度无限制。在下文中,我们将会详细探讨MySQL字符长度无限制的特征和优势。 什么是MySQL字符长度无限制? MySQL字符长度无限制是指在...
在MySQL中,常常会涉及到字符串和数字之间的比较。然而它们有着不同的排序规则,因此需要注意对它们进行正确的比较。 首先我们来看一下数字比较。 SELECT 1 < 2; -- 返回 1 SELECT 2 > 1; -- 返回 1 SELEC...
MySQL是一种流行的关系型数据库管理系统,可以处理各种不同类型的数据。其中字符串是MySQL中最重要的数据类型之一,因为它可以存储各种不同的数据,例如邮件地址、文本信息、数字等等。在MySQL中,有时候我们需要将字符串按照某个符合进行分隔,例如将一条包含多个数字的字符...
在MySQL中,我们经常需要将字符串与变量拼接起来,以便满足数据操作的需求。可以使用CONCAT函数来进行字符串与变量的拼接,下面是一个使用CONCAT函数的例子: SELECT CONCAT('Hello', ' ', 'world'); 这个例子...