MySQL裏面的group by問題淺析

今天做一個備份看板的時候,無意中寫了如下的SQL,當時看到之後就有點疑惑了。

mysql> select backup_date ,count(*) piece_no from redis_backup_result;

+-------------+----------+

| backup_date | piece_no |

+-------------+----------+

| 2018-08-14 | 40906 |

+-------------+----------+

1 row in set (0.03 sec)

一天之內肯定沒有這麼多的記錄,明顯不對,到底是哪裏出了問題呢。

自己仔細看了下SQL,發現是沒有加group by

我們隨機查出10條數據。

mysql> select backup_date from redis_backup_result limit 10;

+-------------+

| backup_date |

+-------------+

| 2018-08-14 |

| 2018-08-14 |

| 2018-08-14 |

| 2018-08-15 |

| 2018-08-15 |

| 2018-08-15 |

| 2018-08-15 |

| 2018-08-15 |

| 2018-08-15 |

| 2018-08-15 |

+-------------+

10 rows in set (0.00 sec)

這個梗很多同學都知道,是在早期的版本中sql_mode默認爲null,不會校驗這個部分,從語法角度來說,是允許的,但是到了高版本,比如5.7之後是不支持的。

mysql> show variables like 'sql_mode%';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| sql_mode | |

+---------------+-------+

1 row in set (0.01 sec)

添加group by之後,結果就符合預期了。

mysql> select backup_date ,count(*) piece_no from redis_backup_result group by backup_date;

+-------------+----------+

| backup_date | piece_no |

+-------------+----------+

| 2018-08-14 | 3 |

| 2018-08-15 | 121 |

| 2018-08-16 | 184 |

| 2018-08-17 | 3284 |

| 2018-08-18 | 7272 |

| 2018-08-19 | 7272 |

| 2018-08-20 | 7272 |

| 2018-08-21 | 7272 |

| 2018-08-22 | 8226 |

+-------------+----------+

9 rows in set (0.06 sec)

但是問題到了這裏,我比較奇怪上面的邏輯,到底是怎麼解析的,看起來是SQL解析了第一行,然後輸出了count(*)的操作。

顯然這個是從執行計劃中無法得到的信息。

mysql> explain extended select backup_date ,count(*) piece_no from redis_backup_result;

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |

| 1 | SIMPLE | redis_backup_result | NULL | ALL | NULL | NULL | NULL | NULL | 38351 | 100.00 | NULL |

+----+-------------+---------------------+------------+------+---------------+------+---------+------+-------+----------+-------+

1 row in set, 2 warnings (0.00 sec)

mysql> show warnings;

| Level | Code | Message

| Warning | 1681 | 'EXTENDED' is deprecated and will be removed in a future release. |

| Note | 1003 | /* select#1 */ select `devopsdb`.`redis_backup_result`.`backup_date` AS `backup_date`,count(0) AS `piece_no` from `devopsdb`.`redis_backup_result` |

2 rows in set (0.00 sec)

我們換個思路。添加sql_mode的約束。

mysql> set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

Query OK, 0 rows affected, 1 warning (0.00 sec)

可以看到這個表有4萬多的記錄。

mysql> select count(*)from redis_backup_result;

+----------+

| count(*) |

+----------+

| 40944 |

+----------+

1 row in set (0.01 sec)

爲了驗證,我們可以使用_rowid的方式來做初步的驗證。

mysql> select _rowid from redis_backup_result limit 5;

+--------+

| _rowid |

+--------+

| 117 |

| 118 |

| 119 |

| 120 |

| 121 |

+--------+

5 rows in set (0.00 sec)

然後可以實現一個初步的思路。

mysql> select _rowid,count(*)from redis_backup_result;

+--------+----------+

| _rowid | count(*) |

+--------+----------+

| 117 | 41036 |

+--------+----------+

1 row in set (0.03 sec)

然後藉助rownum來實現。

mysql> SELECT @rowno:=@rowno+1 as rowno,r._rowid from redis_backup_result r ,(select @rowno:=0) t limit 20;

+-------+--------+

| rowno | _rowid |

+-------+--------+

| 1 | 117 |

| 2 | 118 |

| 3 | 119 |

| 4 | 120 |

| 5 | 121 |

| 6 | 122 |

| 7 | 123 |

| 8 | 124 |

| 9 | 125 |

| 10 | 126 |

| 11 | 127 |

| 12 | 128 |

| 13 | 129 |

| 14 | 130 |

| 15 | 131 |

| 16 | 132 |

| 17 | 133 |

| 18 | 134 |

| 19 | 135 |

| 20 | 136 |

+-------+--------+

20 rows in set (0.00 sec)

寫一個完整的語句。

mysql> SELECT @rowno:=@rowno+1 as rowno,r._rowid ,backup_date,count(*) from redis_backup_result r ,(select @rowno:=0) t ;

+-------+--------+-------------+----------+

| rowno | _rowid | backup_date | count(*) |

+-------+--------+-------------+----------+

| 1 | 117 | 2018-08-14 | 41061 |

+-------+--------+-------------+----------+

1 row in set (0.02 sec)

很明顯是第1行的記錄,然後做了count(*)的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章