在日常的 SQL 開發中,你是否遇到過這樣的報錯:SQL 錯誤 [1055] [42000]: Expression #N of SELECT list is not in GROUP BY clause and contains nonaggregated column...?尤其是在 MySQL 5.7 及以上版本中,這個錯誤更為常見。本文將詳細解析這個錯誤的產生原因,并提供具體的解決方案,幫助你快速定位并解決問題。
一、錯誤現象與核心原因
當執行 SQL 查詢時出現[1055]錯誤,核心原因只有一個:你的查詢違反了sql_mode=only_full_group_by的約束規則。這是 MySQL 中一個重要的 SQL 模式,也是導致該錯誤的直接 “元兇”。
什么是 only_full_group_by?
only_full_group_by是 MySQL 的sql_mode中的一項配置,其核心作用是規范 GROUP BY 查詢的語法邏輯。當啟用該模式時,MySQL 強制要求:SELECT 語句中出現的所有非聚合列(未使用 SUM、COUNT、MAX 等聚合函數的列),必須全部包含在 GROUP BY 子句中。
簡單來說,GROUP BY 的本質是將數據按指定列分組,分組后每組只會保留一條 “代表性” 數據。如果 SELECT 中存在未在 GROUP BY 中聲明的非聚合列,這些列可能在同一分組中存在多個不同值,MySQL 無法確定應該返回哪個值,因此會直接報錯。
二、錯誤實例分析
為了更直觀地理解問題,我們通過一個具體案例來拆解錯誤產生的過程。
場景假設
現有一張訂單表orders,結構如下:
字段名 | 類型 | 說明 |
order_id | int | 訂單 ID(主鍵) |
user_id | int | 用戶 ID |
user_name | varchar | 用戶名 |
order_amount | decimal | 訂單金額 |
create_time | datetime | 下單時間 |
錯誤查詢示例
假設我們需要查詢每個用戶的總訂單金額,編寫了如下 SQL:
SELECT user_id, user_name, SUM(order_amount) AS total_amount,create_time -- 問題列:未在GROUP BY中,也未聚合FROM orders GROUP BY user_id; -- 僅按user_id分組
報錯原因
上述查詢中,create_time是第 4 個字段(對應報錯中的 “Expression #4”),它既沒有出現在 GROUP BY 子句中,也沒有使用聚合函數(如 MAX (create_time))。在only_full_group_by模式下,MySQL 無法確定每個用戶分組應返回哪個create_time(一個用戶可能有多個訂單,對應多個下單時間),因此直接拋出[1055]錯誤。
三、解決方案
針對[1055]錯誤,我們有三種常見的解決思路,每種思路適用于不同場景,需根據實際需求選擇。
方案 1:將非聚合列添加到 GROUP BY 子句
如果 SELECT 中的非聚合列與 GROUP BY 列存在功能依賴關系(即分組列能唯一決定非聚合列的值,如user_id唯一對應user_name),可以將非聚合列直接添加到 GROUP BY 中。
修改后的查詢:
SELECT user_id, user_name, SUM(order_amount) AS total_amount,create_timeFROM orders GROUP BY user_id, user_name, create_time; -- 補充非聚合列到GROUP BY
方案 2:對非聚合列使用聚合函數
如果非聚合列不需要精確值,只需獲取分組中的 “代表性” 數據(如最新時間、最大 ID 等),可以通過聚合函數處理。
修改后的查詢:
SELECT user_id, user_name, SUM(order_amount) AS total_amount,MAX(create_time) AS last_order_time -- 用MAX聚合獲取最新下單時間FROM orders GROUP BY user_id, user_name; -- user_name與user_id功能依賴,需一起分組
方案 3:臨時關閉 only_full_group_by 模式(不推薦)
如果暫時無法調整查詢邏輯,可通過修改sql_mode關閉only_full_group_by。但這是臨時解決方案,可能導致數據歧義,不建議在生產環境使用。
步驟 1:查看當前 sql_mode
SELECT @@sql_mode;
執行后會顯示包含ONLY_FULL_GROUP_BY的模式列表。
步驟 2:臨時關閉(重啟 MySQL 后失效)
SET sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', ''));
步驟 3:永久關閉(需修改配置文件)
- 找到 MySQL 配置文件(Linux 通常是/etc/my.cnf,Windows 是my.ini);
- 在[mysqld]下添加:
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
- 重啟 MySQL 服務。
四、最佳實踐
- 遵循 only_full_group_by 規范:這是最推薦的做法,通過調整查詢邏輯(補充 GROUP BY 列或使用聚合函數)確保 SQL 符合模式要求,避免數據返回歧義。
- 理解功能依賴:如果非聚合列與 GROUP BY 列存在嚴格的一一對應關系(如user_id唯一決定user_name),將非聚合列添加到 GROUP BY 是安全且高效的。
- 避免隨意修改 sql_mode:only_full_group_by是 MySQL 的安全機制,關閉后可能導致查詢結果不可控,尤其在多人協作的項目中應謹慎操作。
總結
SQL 錯誤 [1055]的本質是only_full_group_by模式對 GROUP BY 查詢的語法約束。解決問題的核心在于確保 SELECT 中的非聚合列全部包含在 GROUP BY 中,或通過聚合函數處理。理解這一機制不僅能解決報錯,更能幫助我們寫出更規范、更可靠的 SQL 查詢。