1.环境搭建
CREATE TABLE `temp` (
`key` char(1) DEFAULT NULL,
`value` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO temp ( `key`, `value` )
VALUES
( 'A', 1 ),( 'A', 2 ),( 'A', 3 ),( 'A', 9 ),( 'A', 9 ),
( 'B', 10 ),( 'B', 12 ),( 'B', 63 ),( 'B', 19 ),( 'B', 9 ),
( 'C', 60 ),( 'C', 112 ),( 'C', 6 ),( 'C', 9 ),( 'C', 99 ),
( 'D', NULL ),( 'D', NULL ),( 'D', NULL ),( 'D', NULL ),( 'D', NULL );
2.聚合函数(组函数)
2.1 COUNT
count用于统计次数,可以用于所有类型的字段或常量。
SELECT
`KEY`,
COUNT(*) count01, # 统计出现次数
COUNT(`KEY`) count02, # 可以引用某个字段用于统计
COUNT(`value`) count03, # 但是null不计算次数
COUNT(DISTINCT `value`) count04 # 加上DISTINCT关键字,可以去除重复,得到不同值的数量
FROM
temp
GROUP BY
`KEY`;
/*
+-----+---------+---------+---------+---------+
| KEY | count01 | count02 | count03 | count04 |
+-----+---------+---------+---------+---------+
| A | 5 | 5 | 5 | 4 |
| B | 5 | 5 | 5 | 5 |
| C | 5 | 5 | 5 | 5 |
| D | 5 | 5 | 0 | 0 |
+-----+---------+---------+---------+---------+
4 rows in set (0.04 sec)
*/
2.2 SUM、AVG
sum用于求和,avg用于取平均值,尽可能传递数值类字段/常量。
SELECT
`KEY`,
SUM(`value`) sum01, # 对某个字段求和,null会被忽略
SUM(1) sum02, # 也可以传递常量,sum(n)等于count(*)*n
SUM(DISTINCT `value`) sum03, # 加上DISTINCT关键字,可以去除重复
AVG(`value`) avg01, # 对某个字段去平均值,null会被忽略
AVG(1) avg02, # 也可以传递常量,但是没有实际意义,avg(n)等于n
AVG(DISTINCT `value`) avg03, # 加上DISTINCT关键字,可以去除重复
SUM(`value`) / COUNT(`value`) sc01, # avg等效于sum/count
SUM(DISTINCT `value`) / COUNT(DISTINCT `value`) sc02,
SUM(`KEY`) sumstr01, AVG(`KEY`) avgstr01, # 如果传递非数值类参数,会自动转换类型
SUM('1abc2') sumstr02, AVG(CAST('1abc2' AS SIGNED)) avgstr02 # Mysql的转换机制是"尽力而为",无论隐式还是显式
FROM
temp
GROUP BY
`KEY`;
/*
+-----+-------+-------+-------+---------+--------+---------+---------+---------+----------+----------+----------+----------+
| KEY | sum01 | sum02 | sum03 | avg01 | avg02 | avg03 | sc01 | sc02 | sumstr01 | avgstr01 | sumstr02 | avgstr02 |
+-----+-------+-------+-------+---------+--------+---------+---------+---------+----------+----------+----------+----------+
| A | 24 | 5 | 15 | 4.8000 | 1.0000 | 3.7500 | 4.8000 | 3.7500 | 0 | 0 | 5 | 1.0000 |
| B | 113 | 5 | 113 | 22.6000 | 1.0000 | 22.6000 | 22.6000 | 22.6000 | 0 | 0 | 5 | 1.0000 |
| C | 286 | 5 | 286 | 57.2000 | 1.0000 | 57.2000 | 57.2000 | 57.2000 | 0 | 0 | 5 | 1.0000 |
| D | NULL | 5 | NULL | NULL | 1.0000 | NULL | NULL | NULL | 0 | 0 | 5 | 1.0000 |
+-----+-------+-------+-------+---------+--------+---------+---------+---------+----------+----------+----------+----------+
4 rows in set (0.06 sec)
*/
2.3 MIN、MAX
SELECT
MAX(`KEY`) MAX01, # MAX用于获取最大值
MAX(`value`) MAX02, # NULL依然会被忽略
MAX(DISTINCT `KEY`) MAX03, # 支持DISTINCT关键字,但是没有实际意义
MIN(`KEY`) MIN01, # MIN用于获取最小值,用法和注意事项与MAX完全一致
MIN(`value`) MIN02,
MIN(DISTINCT `KEY`) MIN03
FROM
temp;
/*
+-------+-------+-------+-------+-------+-------+
| MAX01 | MAX02 | MAX03 | MIN01 | MIN02 | MIN03 |
+-------+-------+-------+-------+-------+-------+
| D | 112 | D | A | 1 | A |
+-------+-------+-------+-------+-------+-------+
1 row in set (0.06 sec)
*/
2.4 小结
相同点:
- 仅允许传递一个参数/常量
- 会忽略null
- 可以使用distinct关键字去重
不同点:
- 只有count允许传递*
注意:
上述5种聚合函数,不允许嵌套,例如MAX(MIN(1))。
此外mysql提供了一个拼接字符串的聚合函数group_concat,可参阅:【MySQL基础教程】字符串处理函数
3.GROUP BY
SELECT
`KEY`,
COUNT(*) count01
FROM
temp
GROUP BY
`KEY`, CONCAT('KEY IS ', `KEY`), 'KEY' # 指定分组字段,允许多个字段组合分组,支持表达式和常量,但是传递常量无意义
;
/*
+-----+---------+
| KEY | count01 |
+-----+---------+
| A | 5 |
| B | 5 |
| C | 5 |
| D | 5 |
+-----+---------+
4 rows in set (0.05 sec)
*/
SELECT
`KEY` KEYWORD,
COUNT(*) count01
FROM
temp
GROUP BY
KEYWORD # 也支持使用别名分组
;
/*
+---------+---------+
| KEYWORD | count01 |
+---------+---------+
| A | 5 |
| B | 5 |
| C | 5 |
| D | 5 |
+---------+---------+
4 rows in set (0.05 sec)
*/
SELECT
`KEY` KEYWORD,
COUNT(*) count01
FROM
temp
GROUP BY
1 # 还支持使用字段序号,从1开始
;
/*
+---------+---------+
| KEYWORD | count01 |
+---------+---------+
| A | 5 |
| B | 5 |
| C | 5 |
| D | 5 |
+---------+---------+
4 rows in set (0.05 sec)
*/
SELECT
`KEY` KEYWORD,
COUNT(*) count01
FROM
temp
GROUP BY
1 DESC # 还可以写入排序,简化SQL语句
;
/*
+---------+---------+
| KEYWORD | count01 |
+---------+---------+
| D | 5 |
| C | 5 |
| B | 5 |
| A | 5 |
+---------+---------+
4 rows in set (0.05 sec)
*/
SELECT
`KEY`,
`value`,
COUNT(*) count01
FROM
temp
GROUP BY
`KEY`, `value`
WITH ROLLUP # 带上WITH ROLLUP,可以实现小计+总计的功能:分别以KEY+VALUE,KEY,NULL(可以看成是常量,所有数据在一个组中)分组
;
+------+-------+---------+
| KEY | value | count01 |
+------+-------+---------+
| A | 1 | 1 |
| A | 2 | 1 |
| A | 3 | 1 |
| A | 9 | 2 |
| A | NULL | 5 |
| B | 9 | 1 |
| B | 10 | 1 |
| B | 12 | 1 |
| B | 19 | 1 |
| B | 63 | 1 |
| B | NULL | 5 |
| C | 6 | 1 |
| C | 9 | 1 |
| C | 60 | 1 |
| C | 99 | 1 |
| C | 112 | 1 |
| C | NULL | 5 |
| D | NULL | 5 |
| D | NULL | 5 |
| NULL | NULL | 20 |
+------+-------+---------+
20 rows in set (0.06 sec)
4.HAVING
SELECT
`KEY`,
COUNT(*) count01
FROM
temp
GROUP BY
`KEY`
HAVING
SUM(`VALUE`) > 100 # 过滤数据,语法与WHERE一致,但是HAVING是在分组聚合之后过滤,WHERE在GROUP BY之前
;
/*
+-----+---------+
| KEY | count01 |
+-----+---------+
| B | 5 |
| C | 5 |
+-----+---------+
2 rows in set (0.05 sec)
*/
SELECT
`KEY`,
COUNT(*) count01
FROM
temp
HAVING
SUM(`VALUE`) > 100 # 如果在没有GROUP BY字句的情况下使用HAVING,相当于GROUP BY 常量,所有的数据会放在一个组中
;
/*
+-----+---------+
| KEY | count01 |
+-----+---------+
| A | 20 |
+-----+---------+
1 row in set (0.05 sec)
*/
SELECT
`KEY`,
COUNT(*) count01
FROM
temp
GROUP BY
`KEY`
HAVING
`value` < 100 # 既没有出现在GROUP BY字句,又没有使用聚合函数包裹的列,不能使用HAVING过滤
;
/*
1054 - Unknown column 'value' in 'having clause'
*/