mysql聚合函数使用
MySQL里的合函聚合函数,说穿了就是数使干一件事:把多行数据压成一行结果。没有它们,合函你写SQL做统计就得先把数据拉到程序里,数使再循环累加,合函又慢又蠢。数使直接上例子,合函看个真实的数使场景。
我手头有个订单表orders,合函结构大概是数使order_id, user_id, amount, created_at。老板要月报,合函问这个月总共收了多少钱,数使多少笔订单。合函没有聚合函数,数使你得SELECT * FROM orders WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31',合函然后代码里for循环加。有了COUNT和SUM,一行搞定:
SELECT COUNT(*), SUM(amount) FROM orders WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31'; 返回两列,一个总笔数,一个总金额。就这么直接。
但别急着高兴,很多新手栽在COUNT(*)和COUNT(column)的区别上。COUNT(*)数所有行,包括某列全是NULL的行。COUNT(amount)只数amount非NULL的行。比如你统计“有多少用户填了手机号”,表里phone字段有NULL,你用COUNT(phone)得到的是填了的数量,用COUNT(*)得到的是总行数,两者差出来的就是没填的。这个坑我见过不止一次,报表数字对不上,查了半天发现是COUNT用错了参数。
再说GROUP BY,这才是聚合函数的灵魂搭档。没分组,聚合函数作用于全表;有分组,它作用于每组内。还是那个订单表,你想看每个用户的消费总额,不加GROUP BY就只能得到全公司的总数,加了它:
SELECT user_id, SUM(amount) FROM orders GROUP BY user_id; 结果按用户分组,每人一行,带各自累计金额。注意,SELECT后面只能出现GROUP BY里写过的列,或者聚合函数结果。你要是手贱写成SELECT user_id, order_id, SUM(amount),MySQL在ONLY_FULL_GROUP_BY模式下直接报错,因为order_id不属于分组键,系统不知道该取哪一行。这个报错很常见,别慌,把order_id去掉或者也放进GROUP BY就行。
HAVING这个关键字,很多人和WHERE搞混。WHERE在分组前过滤行,HAVING在分组后过滤组。比如你想找出消费总额超过1000的用户,不能写WHERE SUM(amount) > 1000,因为WHERE执行时SUM还没算出来。必须这样:
SELECT user_id, SUM(amount) AS total FROM orders GROUP BY user_id HAVING total > 1000; 这里我用了别名total,HAVING里可以直接引用,MySQL允许,但WHERE里不能引别名。这是语法细节,但实际写多了就自然记住了。
还有个冷门但实用的:GROUP_CONCAT。它能把分组内某列的值拼成一个字符串。比如你想列出来每个用户最近的三个订单ID,用GROUP_CONCAT配合ORDER BY和LIMIT(MySQL 8.0支持):
SELECT user_id, GROUP_CONCAT(order_id ORDER BY created_at DESC SEPARATOR ',') FROM orders GROUP BY user_id; 返回的是一串逗号分隔的ID。这个函数在生成报表、做数据透视时特别省事,但注意结果长度受group_concat_max_len限制,默认1024字节,超了会被截断。大数据量时记得调大这个会话变量。
最后提醒一句:聚合函数不处理NULL。SUM、AVG遇到NULL直接跳过,不会把NULL当0。如果你的金额字段允许NULL,统计前最好用COALESCE(amount, 0)包一层,否则SUM的结果可能比预期小。这属于脏数据问题,但现实中表结构不严谨太常见了。
用多了就知道,聚合函数本身不难,难的是搞清楚数据里哪些是NULL、哪些需要分组、过滤发生在哪一步。把上面这几个点理顺,写统计SQL基本不会卡壳。
(责任编辑:百科)














