MySQL之——GROUP BY分組取欄位最大值
阿新 • • 發佈:2018-11-08
出處:http://blog.csdn.net/l1028386804/article/details/54657412
假設有一個業務場景,需要查詢使用者登入記錄資訊,其中表結構如下:
[sql] view plain copy- CREATE TABLE `tb` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `uid` int(11) NOT NULL,
- `ip` varchar(16) NOT NULL,
- `login_time` datetime,
- PRIMARY KEY (`id`),
- KEY (`uid`)
- );
[sql] view plain copy
- INSERT INTO tb SELECT null, 1001, '192.168.1.1', '2017-01-21 16:30:47';
- INSERT INTO tb SELECT null, 1003, '192.168.1.153'
- INSERT INTO tb SELECT null, 1001, '192.168.1.61', '2017-01-21 16:50:41';
- INSERT INTO tb SELECT null, 1002, '192.168.1.31', '2017-01-21 18:30:21';
- INSERT INTO tb SELECT null, 1002, '192.168.1.66', '2017-01-21 19:12:32';
- INSERT INTO tb SELECT null, 1001, '192.168.1.81', '2017-01-21 19:53:09';
- INSERT INTO tb SELECT null, 1001, '192.168.1.231', '2017-01-21 19:55:34';
[plain] view plain copy
- +----+------+---------------+---------------------+
- | id | uid | ip | login_time |
- +----+------+---------------+---------------------+
- | 1 | 1001 | 192.168.1.1 | 2017-01-21 16:30:47 |
- | 2 | 1003 | 192.168.1.153 | 2017-01-21 19:30:51 |
- | 3 | 1001 | 192.168.1.61 | 2017-01-21 16:50:41 |
- | 4 | 1002 | 192.168.1.31 | 2017-01-21 18:30:21 |
- | 5 | 1002 | 192.168.1.66 | 2017-01-21 19:12:32 |
- | 6 | 1001 | 192.168.1.81 | 2017-01-21 19:53:09 |
- | 7 | 1001 | 192.168.1.231 | 2017-01-21 19:55:34 |
- +----+------+---------------+---------------------+
[html] view plain copy
- SELECT uid, max(login_time)
- FROM tb
- GROUP BY uid;
- +------+---------------------+
- | uid | max(login_time) |
- +------+---------------------+
- | 1001 | 2017-01-21 19:55:34 |
- | 1002 | 2017-01-21 19:12:32 |
- | 1003 | 2017-01-21 19:30:51 |
- +------+---------------------+
[sql] view plain copy
- -- 錯誤寫法
- SELECT uid, ip, max(login_time)
- FROM tb
- GROUP BY uid;
- -- 錯誤寫法
(如果sql_mode開啟了only_full_group_by,則不會執行成功。)
可能ip欄位會取uid分組前的第一個row的值,顯然不是所需資訊
寫法1
寫一個子查詢:
[sql] view plain copy
- SELECT a.uid, a.ip, a.login_time
- FROM tb a
- WHERE a.login_time in (
- SELECT max(login_time)
- FROM tb
- GROUP BY uid);
再或者換一個寫法:
[sql] view plain copy
- SELECT a.uid, a.ip, a.login_time
- FROM tb a
- WHERE a.login_time = (
- SELECT max(login_time)
- FROM tb
- WHERE a.uid = uid);
在5.6以前的版本中,寫法②這條sql在大資料量的情況下,執行計劃不理想,目測效能不佳。
在5.6及以後的版本中,寫法②這條sql會快很多,執行計劃也有了改變
5.5.50:
[plain] view plain copy
- +----+--------------------+-------+------+---------------+------+---------+------+------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+--------------------+-------+------+---------------+------+---------+------+------+-------------+
- | 1 | PRIMARY | a | ALL | NULL | NULL | NULL | NULL | 7 | Using where |
- | 2 | DEPENDENT SUBQUERY | tb | ALL | uid | NULL | NULL | NULL | 7 | Using where |
- +----+--------------------+-------+------+---------------+------+---------+------+------+-------------+
[plain] view plain copy
- +----+--------------------+-------+------+---------------+------+---------+------------+------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+--------------------+-------+------+---------------+------+---------+------------+------+-------------+
- | 1 | PRIMARY | a | ALL | NULL | NULL | NULL | NULL | 7 | Using where |
- | 2 | DEPENDENT SUBQUERY | tb | ref | uid | uid | 4 | test.a.uid | 1 | NULL |
- +----+--------------------+-------+------+---------------+------+---------+------------+------+-------------+
直接改成join效能會更加好:
[sql] view plain copy
- SELECT a.uid, a.ip, a.login_time
- FROM (SELECT uid, max(login_time) login_time
- FROM tb
- GROUP BY uid
- ) b JOIN tb a ON a.uid = b.uid AND a.login_time = b.login_time;
[plain] view plain copy
- +------+---------------+---------------------+
- | uid | ip | login_time |
- +------+---------------+---------------------+
- | 1003 | 192.168.1.153 | 2017-01-21 19:30:51 |
- | 1002 | 192.168.1.66 | 2017-01-21 19:12:32 |
- | 1001 | 192.168.1.231 | 2017-01-21 19:55:34 |
- +------+---------------+---------------------+