
本文首發:
MySQL only_full_group_by 1055報錯的三種解決方案,臨時關閉有影響嗎?當我們遷移到 MySQL 5.7+ 的版本時,常會碰到
ERROR 1055 only_full_group_by
錯誤,這是 5.7 之後
SQL_MODE
預設打開了嚴格模式導緻的錯誤。說明你代碼裡有地方寫的不嚴謹。
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause
and contains nonaggregated column 'kalacloud.user_id' which is not functionally
dependent on columns in GROUP BY clause; this is incompatible
with sql_mode=only_full_group_by
我看到大多數教程,隻寫了解決這個問題「術」的部分,并沒有講解什麼原因導緻這個錯誤。本教程先從原理講起,先讓大家了解為什麼會出錯。然後給出三種解決方案:「徹底解決」、「臨時解決」和「折中解決」,你可根據自己的實際情況進行選擇。
SQL_MODE 是什麼?
講
ONLY_FULL_GROUP_BY
錯誤前,我們先來說一下
SQL_MODE
。了解 MySQL 工作原理能更好的幫你了解錯誤發生的本質原因。
SQL_MODE
是 MySQL 中的一個環境變量,定義了 MySQL 支援的 SQL 文法和資料校驗程度。
SQL_MODE
一共三種模式
- ANSI 模式:寬松模式。對插入資料進行校驗,如不符合定義類型或長度,對儲存資料進行截斷。
- TRADITIONAL 模式:嚴格模式。對插入資料進行嚴格校驗,保證錯誤資料不能插入,ERROR 報錯。用于事物時,事物會進行復原。
- STRICT_TRANS_TABLES 模式:嚴格模式。對插入資料進行嚴格校驗,錯誤資料不能插入,ERROR 報錯。
MySQL 5.7.4 之前,MySQL 預設不開啟嚴格模式
這是 MySQL 更新到5.7.5 之後預設SQL_MODE 為嚴格模式:
擴充閱讀:
MySQL 連接配接數過多的處理方法合集 - Too many connectionsSQL_MODE 嚴格模式的意義
在寬松模式下,即便 insert 一個錯誤的資料,MySQL 也會不加判斷的全盤接受。
我們來看一個執行個體
我們首先關閉
SQL_MODE
嚴格模式
set session sql_mode='';
然後我們建立一個表并向其中插入一組超範圍的資料
create table kalacloud_t1(website char(9));
insert into kalacloud_t1 values('kalacloud.com');
傳回值:
從傳回值可以看出,我們向
websie
cher(9)
中插入了一條長為 13 的值,沒有報錯,直接插入,但超過 9 的部分,即「.com」被截斷丢掉了。
接着我們在嚴格模式下試試,首先打開
SQL_MODE
嚴格模式:
set session sql_mode='TRADITIONAL';
create table kalacloud_t2(website char(9));
insert into kalacloud_t2 values('kalacloud.com');
我們可以從傳回值看出,MySQL 直接報錯,告訴你插入的資料有問題。
MySQL 配置檔案 my.cnf / my.ini 逐行詳解ONLY_FULL_GROUP_BY 問題及解決方案
接着我們來說說
ONLY_FULL_GROUP_BY
的問題。當我們資料庫遷移至 5.7 或者 8.0 之後,最常見的錯誤就是
Error 1055 only_full_group_by
錯誤。
正如我前文所寫,這個錯誤的關鍵原因是不規範的 SQL 文法,5.7 之後預設
SQL_MODE
變為嚴格模式。
我們來一起看一個執行個體,這是一組
卡拉雲使用者點選網頁的 log 記錄:
現在我們使用
GROUP BY
來排序找出通路量最大的網頁。
我們先關掉
sql_mode
的嚴格模式來試試:
在寬松模式下,我們可以看出這個 query 雖然可以查詢,但文法和邏輯上稍有問題。我們想對
page_url
進行排序,但 query 中也加入了
user_id
,在傳回值中可以發現問題,index.html 這個頁面不僅 user_id 1 的使用者通路過,使用者 2 和 3 也通路了,那麼這張傳回的表表格資料就是有問題的。
user_id 1 列在傳回資料裡,到底代表什麼?是第一個通路 index.html 還是最後一個通路這個頁面的意思呢?沒人知道,這是個随機盲盒,運作原理未知。
我們打開
sql_mode
嚴格模式跑一下上面這段代碼:
傳回一個 ERROR 1055 報錯。
ONLY_FULL_GROUP_BY
是
SQL_MODE
中
TRADITIONAL
的選項參數,從 5.7 開始預設開啟為嚴格模式。這就是為什麼大家遷移到 MySQL 新版會報 1055 錯誤的原因。
我們已經了解了這個問題的原理原因,接下來,碰到
ONLY_FULL_GROUP_BY
報錯,我們應該怎麼處理呢?
如何在 MySQL 中查詢資料庫中帶有某個字段的所有表名解決方案 1 – 重寫代碼
找到報錯文法中錯誤的部分,根據邏輯重寫 query,本示例中,我們去掉
user_id
即可。
如果你有大量在舊版 SQL 完成的代碼,檢查這種錯誤很有可能是相當浩大的工程。這時就到了你的決斷時刻了,你有兩條路可選:
(1)幹脆推翻重寫,讓代碼保持嚴謹和整潔,養成良好的習慣,避免未來出現的未知錯誤。
(2)了解原理。知道可能會出現的問題,關掉嚴格模式,回到寬松模式,接着用舊版代碼。
解決方案 2 – 傳回寬松模式
如果你碰到 ERROR 1055 報錯,但也不想在浩如煙海的代碼中糾錯,那麼你可以回到「寬松模式」接着用。
在 MySQL 5.7 及以上版本中 SQL_MODE 包含
ONLY_FULL_GROUP_BY、STRINCT_TRANS_TABLES、NO_ZERO_IN_DATE、NO_ZERO_DATE、ERROR_FOR_DIVISION_BY_ZERO、NO_AUTO_CREATE_USER
我們直接在 MySQL 配置檔案中更改,或者臨時全部關閉:
SET GLOBAL sql_mode='';
或者單關閉
ONLY_FULL_GROUP_BY
MariaDB [kalacloud_demo]> SET GLOBAL sql_mode='STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_ENGINE_SUBSTITUTION';
解決方案 3 – 使用聚合函數
如果某些特别的原因就是要查詢
user_id
,但你又沒空改代碼,那麼我可使用聚合函數來規避這類錯誤,算是一種折中的方案了,文法改嚴謹了,代碼又不需要大動。
我們可以使用
MAX()
、
MIN()
或者
GROUP_CONCAT()
聚合函數。
MySQL 還提供了
ANY_VALUE()
函數,來解決這類問題:
總結和推薦
在代碼嚴謹、清晰的道路上走多遠都不為過,我個人推薦使用第一種解決方案,趁着這一次機會,把你代碼中潛藏的隐患一網打盡。之後遵循嚴謹的代碼邏輯來寫代碼。當然,有很多特殊情況,你也可以參照方案二、三來臨時處理。
接着推薦一下我開發的
,隻要你會寫 MySQL ,就能使用卡拉雲搭建自己的資料工具,比如,資料看闆,企業 CRM、ERP,權限管理背景,對賬系統等。
卡拉雲是新一代低代碼開發工具,免安裝部署,可一鍵接入包括 MySQL 在内的常見資料庫及 API。可根據自己的工作流,定制開發。無需繁瑣的前端開發,隻需要簡單拖拽,即可快速搭建企業内部工具。數月的開發工作量,使用卡拉雲後可縮減至數天。
卡拉雲可一鍵接入常見的資料庫及 API
卡拉雲可根據公司工作流需求,輕松搭建資料看闆或其他内部工具,并且可一鍵分享給組内的小夥伴。
下圖為使用卡拉雲在 5 分鐘内搭建的「
優惠券發放核銷」背景,僅需要簡單拖拽即可快速生成前端元件,隻要會寫 SQL,便可搭建一套趁手的資料庫工具。立即使用
。
有關 MySQL 教程,可繼續拓展學習: