拿 employee
示例數據庫為例,當進行如下操作時會報錯。
mysql> SELECT * FROM employees GROUP BY gender;
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'employees.employees.emp_no' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
其中 employee
的表結構為:
mysql> DESCRIBE employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.01 sec)
原因
SQL 標准中不允許 SELECT 列表,HAVING 條件語句,或 ORDER BY 語句中出現 GROUP BY 中未列表的可聚合列。而 MySQL 中有一個狀態 ONLY_FULL_GROUP_BY 來標識是否遵從這一標准,默認為開啟狀態。
所以這樣的語句是不可以的,
# 🚨
SELECT gender,
last_name
FROM employees
GROUP BY gender
將 last_name
從 SELECT 中移除或將其添加到 GROUP BY 中都可以修復。
# ✅
SELECT gender,
FROM employees
GROUP BY gender
# ✅
SELECT gender,
last_name
FROM employees
GROUP BY gender,
last_name
但這樣的修改查詢出來就可能就不是想要的結果了。
解決
三種方式來解決。
關閉 ONLY_FULL_GROUP_BY
可以選擇關掉 MySQL 的 ONLY_FULL_GROUP_BY 模式。
有兩種方式,通過昨晚設置 sql_mode
來關閉。
首先查看變更前的 sql_mode
:
mysql> SELECT @@sql_mode;
+-----------------------------------------------------------------------------------------------------------------------+
| @@sql_mode |
+-----------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
通過以下腳本關閉 :
SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY,',''));
再次查詢 @@sql_mode
返回中應該已經沒有該模式了。
mysql> SELECT @@sql_mode;
+----------------------------------------------------------------------------------------------------+
| @@sql_mode |
+----------------------------------------------------------------------------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+----------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
第二種是找到 MySQL 配置文件修改並保存。
MySQL 的配置文件名為 my.cnf
,可通過以下命令查看你位置:
$ mysql --help | grep cnf
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /usr/local/etc/my.cnf ~/.my.cnf
找到后編輯並保存,重啟 MySQL 后生效。
[mysqld]
-sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
+sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
如果文件中沒有 sql_mode
配置項可手動添加上。
因為 ONLY_FULL_GROUP_BY
更加符合 SQL 標准,所以不建議關掉。
ANY_VALUE()
還可以通過 ANY_VALUE() 來改造查詢語句以避免報錯。
使用 ANY_VALUE()
包裹的值不會被檢查,跳過該錯誤。所以這樣是可以的:
SELECT gender,
- last_name
+ ANY_VALUE(last_name)
FROM employees
GROUP BY gender
添加列間的依賴
像這個示例中,
# 🚨
SELECT gender,
last_name
FROM employees
GROUP BY gender
假如我們讓 gender
變成不重復的主鍵,last_name
便與 gender
產生了一種關系,即 gender
可唯一確定 last_name
。此時便可進行 GROUP BY
了。因為,之所以報錯是因為在進行聚合的時候有不能確定的列參與了進來。
總結
一般 GROUP BY 會與另外的聚合函數配合使用,比如 COUNT(), SUM() 等。查詢所有列無差別地進行 GROUP BY 的情況並不是正常的使用姿勢。