zoukankan      html  css  js  c++  java
  • 关于MAX()函数的一点思考

    本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/103

    考虑如下表和sql:

    CREATE TABLE `iknow_team_info` (
      `teamId` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
      `userNum` int(11) unsigned NOT NULL DEFAULT '0'',
      PRIMARY KEY (`teamId`),
    ) ENGINE=InnoDB  DEFAULT CHARSET=gbk'
    
    mysql> select teamId,userNum from iknow_team_info limit 10; 
    +--------+---------+
    | teamId | userNum |
    +--------+---------+
    |      1 |      73 |
    |      4 |     100 |
    |      8 |     112 |
    |      9 |     136 |
    |     10 |      58 |
    |     12 |      84 |
    |     16 |     141 |
    |     17 |     560 |
    |     18 |     114 |
    |     19 |       8 |
    +--------+---------+
    10 rows in set (0.01 sec)
    
    mysql> select teamId,max(userNum) from iknow_team_info;
    +--------+--------------+
    | teamId | max(userNum) |
    +--------+--------------+
    |      1 |         1000 |
    +--------+--------------+
    1 row in set (0.02 sec)
    

    关于最后一个sql:查找人数(userNum)最多的行对应的teamId,为什么会返回1呢?很显然人数最多的行对应的teamId不是1。

    在这里userNum列没有索引,mysql肯定会全表扫描:

    mysql> explain select teamId,max(userNum) from iknow_team_info;
    +----+-------------+-----------------+------+---------------+------+---------+------+-------+-------+
    | id | select_type | table           | type | possible_keys | key  | key_len | ref  | rows  | Extra |
    +----+-------------+-----------------+------+---------------+------+---------+------+-------+-------+
    |  1 | SIMPLE      | iknow_team_info | ALL  | NULL          | NULL | NULL    | NULL | 12191 |       |
    +----+-------------+-----------------+------+---------------+------+---------+------+-------+-------+
    1 row in set (0.00 sec)
    

    我猜测的sql执行过程是这样的: 全表扫描,扫描的过程中记录下扫描过得最大的userNum以及对应的teamId,最后将结果返回。这个过程应该很清晰明了,为什么mysql没有返回正确结果呢?

    最后在官方手册中寻找到了答案:

    原来MAX()也是聚集函数的一种,所有聚集函数如下表:
    image

    当我们使用了上面表中的聚集函数但是却没有包含group by时,mysql会默认在所有满足条件的行上做聚集。

    If you use a group function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows.

    所以我们可以大胆的假设上面的sql等同于:

    mysql> select teamId,max(userNum) from iknow_team_info group by null;
    +--------+--------------+
    | teamId | max(userNum) |
    +--------+--------------+
    |      1 |         1000 |
    +--------+--------------+
    1 row in set (0.02 sec)
    

    mysql跟标准sql的一点不同是:mysql接受出现在select列表中但是没有出现在group by列表中的列。所以,当teamId不在group by的列表中时,mysql会在每一个分组中随机挑选出一个teamId,所以最后出现的teamId是1,不是正确的。

    If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard SQL use of GROUP BY permits the select list, HAVING condition, or ORDER BY list to refer to nonaggregated columns even if the columns are not functionally dependent on GROUP BY columns. This causes MySQL to accept the preceding query. In this case, the server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate, which is probably not what you want.

    要想出现正确的结果,我们可以按照下面的方式书写sql:

    mysql> select teamId,userNum from iknow_team_info order by userNum desc limit 1;
    +--------+---------+
    | teamId | userNum |
    +--------+---------+
    |  88010 |    1000 |
    +--------+---------+
    1 row in set (0.01 sec)
    

    或者我们可以让teamId出现在group by的列表中,从而取出正确的teamId(即列出每个teamId组内的max(userNum)),再对所有的max(userNum)进行排序。

    mysql> select teamId,max(userNum) maxNum from iknow_team_info group by teamId order by maxNum desc limit 1;
    +--------+--------+
    | teamId | maxNum |
    +--------+--------+
    |  88041 |   1000 |
    +--------+--------+
    1 row in set (0.02 sec)
    

    参考资料:
    How does SQL MAX() works?
    Aggregate (GROUP BY) Function Descriptions

  • 相关阅读:
    L1-049 天梯赛座位分配​​​​​​​
    L1-046 整除光棍 大数除法
    天梯赛 L1-043 阅览室
    Hdu 1022 Train Problem I 栈
    蓝桥杯 历届试题 格子刷油漆  (动态规划)
    第九届蓝桥杯省赛真题 日志统计
    2018年第九届蓝桥杯第7题 螺旋折线
    2018年第九届蓝桥杯省赛 递增三元组
    蓝桥杯 历届试题 高僧斗法  (尼姆博弈)
    K-th Number
  • 原文地址:https://www.cnblogs.com/zhangyachen/p/8033276.html
Copyright © 2011-2022 走看看