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

  • 相关阅读:
    Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)
    IP地址资源的分配和管理
    破解中常见的指令及修改
    8086 CPU 寻址方式
    汇编指令速查
    关于ida pro的插件keypatch
    动态方式破解apk进阶篇(IDA调试so源码)
    IDA7.0安装keypatch和findcrypt-yara插件
    Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码)
    IDA动态调试技术及Dump内存
  • 原文地址:https://www.cnblogs.com/zhangyachen/p/8033276.html
Copyright © 2011-2022 走看看