MySQL GROUP BY 的問題


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 的情況並不是正常的使用姿勢。

相關資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM