原文鏈接:https://blog.csdn.net/hq091117/article/details/79065199
https://blog.csdn.net/allen_tsang/article/details/54892046
MySQL 5.7.5后only_full_group_by成為sql_mode的默認選項之一,這可能導致一些sql語句失效。
比如下表game:
| id | group_id | name | score |
|---|---|---|---|
| 1 | A | 小剛 | 20 |
| 2 | B | 小明 | 19 |
| 3 | B | 小花 | 17 |
| 4 | C | 小紅 | 18 |
執行sql:
select name, group_id from game group by group_id(記為sql_make_group)
返回:
Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'game.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
解決方法
-
把group by字段
group_id設成primary key 或者 unique NOT NULL。這個方法在實際操作中沒什么意義。 -
使用函數
any_value把報錯的字段name包含起來。如,select any_value(name), group_id from game group by group_id。 -
在配置文件my.cnf中關閉sql_mode=ONLY_FULL_GROUP_BY.。msqyl的默認配置是
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。可以把ONLY_FULL_GROUP_BY去掉,也可以去掉所有選項設置成sql_mode=,如果你確信其他選項不會造成影響的話。
成功的步驟:
命令行打開mysql.cnf,可以用whereis查找
sudo vim /etc/mysql/conf.d/mysql.cnf
滾動到文件底部復制並粘貼
[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
wq保存並退出
重啟MySQL
sudo service mysql restart
執行成功后,返回結果應該是
| name | group_id |
|---|---|
| 小剛 | A |
| 小明 | B |
| 小紅 | C |
為什么默認設置ONLY_FULL_GROUP_BY限制?
對於上述的報錯信息,我的理解是select字段里包含了沒有被group by條件唯一確定的字段name。
因為執行sql_make_group語句實際上把兩行紀錄小明和小花合並成一行,搜索引擎不知道該返回哪一條,所以認為這樣的sql是武斷的(arbitrary).
解決辦法2和3都是禁止檢查返回結果的唯一性。
進階討論
上述辦法可以解決報錯的問題,但也說明sql語句本身有邏輯問題。name字段不應該出現在返回結果,因為它是不確定的。
考慮這樣的需求:按group_id分組后,找出每組得分最少的人。
執行sql: select any_value(name) as name, group_id, min(score) as score from game group by group_id order by min(score)
返回結果
| name | group_id | score |
|---|---|---|
| 小明 | B | 17 |
| 小紅 | C | 18 |
| 小剛 | A | 20 |
B組的name是小明(因為小明的id更小),而期望結果應該是小花。
所以單純使用group by無法實現這樣的需求。可以使用臨時表的方法:
select id, name,t.group_id, t.score from (select group_id, min(score) as score from game group by group_id order by min(score)) t inner join game on t.group_id=game.group_id and t.score=game.score
