分類列表想同時顯示每類筆數與最新一筆名稱,SQL卻回傳1055錯誤。別先關掉ONLY_FULL_GROUP_BY。這個錯誤通常是在問你:同一組裡有多個名稱,結果只留一列時,到底要顯示哪一個?先回答這個問題,比讓查詢勉強執行更有用。
分組後,一般欄位可能沒有唯一答案
先在出錯的同一條連線查SELECT @@SESSION.sql_mode;,確認目前是否包含ONLY_FULL_GROUP_BY。開發工具、應用程式連線與另一個終端可能使用不同連線設定;只讀了全域設定,未必解釋應用程式正在使用的模式。診斷時保留版本與當前連線值,不必先改伺服器設定。
GROUP BY category把同類資料集合成一組,COUNT(*)可以計算組內列數。但name沒有經過聚合,同一分類若有Alpha、Beta與Aardvark,就沒有一個自然且唯一的name可放到結果中。下列查詢在本案例MySQL 8.4.9啟用ONLY_FULL_GROUP_BY時被拒絕,錯誤碼是1055。
SELECT category, name, COUNT(*)
FROM items
GROUP BY category;
這不是所有未列在GROUP BY中的欄位都永遠禁止。MySQL也會判斷功能依賴,例如分組鍵能唯一識別一筆資料時,其他欄位可能有唯一答案;文件也列有WHERE限制為單一值的情況。本例category既不是唯一鍵,同類又有不同name,因此不能靠這些例外取得所需結果。
把name也加入GROUP BY,雖然可能讓查詢合法,卻把「每個分類一組」改成「每個分類與名稱的組合一組」。如果你要的是分類總數,這不是原來的需求。修正SQL前先列出結果應有幾列,以及每列代表什麼,避免只修掉錯誤訊息。
建立可分辨最大名稱與最新資料的案例
下面在自有隔離測試資料庫使用四筆資料。A分類有三個名稱,最新日期刻意配Aardvark;按目前測試排序規則比較字串時,最大名稱則是Beta。這樣兩種需求會得到不同答案,不會因為測試資料巧合而誤以為MAX等於最新。
CREATE TABLE items (
id INT PRIMARY KEY,
category VARCHAR(10),
name VARCHAR(30),
created_at DATETIME
);
INSERT INTO items VALUES
(1,'A','Alpha','2026-09-01'),
(2,'A','Beta','2026-09-02'),
(3,'B','Gamma','2026-09-01'),
(4,'A','Aardvark','2026-09-03');
本次案例只在當前連線設定測試用sql_mode,再執行負測與兩種正確查詢;沒有修改正式伺服器的全域模式。負測輸出的1055保留在測試紀錄,命令行允許後續語句繼續執行,所以整段程序最後返回成功也不代表所有SQL都成功。
MAX(name)回答的是最大字串
SELECT category,
MAX(name) AS greatest_name,
COUNT(*) AS total
FROM items
GROUP BY category;
本次A組得到greatest_name=Beta、total=3;B組得到Gamma、1。查詢清楚地要求字串最大值,所以是合理的分組聚合。但它沒有查詢日期,不能回答「最新建立的名稱」。字串比較還受欄位排序規則影響,不要把MAX理解成插入順序或肉眼覺得較新的名稱。
如果需求確實只是統計每類筆數,直接選category與COUNT(*)就好,不必額外加一個沒有用途的名稱。若要每類最大金額,MAX(amount)也有明確意義;但最大金額與其他欄位分別聚合後,可能來自不同原始列。需要整列資料時,應另寫選列規則。
COUNT(*)計算列數,COUNT(name)則不計入名稱為NULL的列。本例每列都有名稱,所以沒有測出兩者差異;套用到實際報表時,別只因結果暫時相同就互換統計式。
同樣地,ANY_VALUE(name)只是讓你接受組內某個值,不是最新列的別名。在本題裡使用它會跳過「選哪一列」的責任。把ONLY_FULL_GROUP_BY關掉也無法替你補上日期規則,還可能讓讀者以為任意結果是業務要求。
在分組查詢最後加ORDER BY created_at DESC,也不能把組內的name變成最新資料。結果列的排序與組內取值是不同工作;先選出什麼結果,再排列結果,不能倒過來解讀。若錯誤查詢沒有得到合法結果,就更不能用後面的ORDER BY來證明某個名稱會被選中。
要最新整列,用視窗排序選第一列
SELECT category, name
FROM (
SELECT category, name,
ROW_NUMBER() OVER (
PARTITION BY category
ORDER BY created_at DESC, id DESC
) AS rn
FROM items
) AS ranked
WHERE rn = 1
ORDER BY category;
PARTITION BY先把每個分類視為一個計算範圍;created_at DESC讓較新的時間先排,id DESC則處理同時建立的平手。ROW_NUMBER給每列一個序號,外層只留下rn=1。這次A分類得到Aardvark,B分類仍是Gamma,與MAX(name)的A組結果不同。
這段使用MySQL 8.4支援的視窗函式,不能直接當成MySQL 5.7也可執行的範例。它先計算列序號,再由外層篩選,並沒有在同一層WHERE直接使用rn。複製程式時保留外層查詢與別名,才能依同樣的計算順序執行。
若日期可能為NULL,還要決定沒有日期的資料是否能成為最新項目;若業務允許同時多筆並列最新,ROW_NUMBER只取一筆就不符合需求。這篇用非空且不同的測試日期,加上唯一id選定一筆,不假裝已驗過你實際資料裡所有時間與並列規則。
先定義每組答案,再處理效能
只取MAX(created_at)再JOIN回原表,也要處理同時間多筆的情況。每類最大時間可以是唯一的一個時間值,對應的原始資料卻可能有兩列;不加其他規則就會得到多筆。ROW_NUMBER範例用id作最後一層,直接表達「每組只取一列」的選擇,而不是假設時間天然唯一。
把結果攤在一起讀最容易辨認:A分類共三筆,字串最大是Beta,最新日期的名稱是Aardvark;B分類只有一筆,所以兩種查詢都得到Gamma。單筆分類讓錯誤寫法看起來合理,多筆且刻意不一致的分類才真正驗證了需求。測試資料不能全部剛好符合你的猜測。
驗收時準備三種資料:同組多個不同名稱、字串最大值不是最新列,以及時間相同但id不同。前兩種已在這個隔離案例對照;第三種可用來確認你選定的平手規則。若產品認為同時間應取較小id,就同步改為id ASC,不能讓查詢與說明互相矛盾。
分類筆數與最新列可以分別查詢,也可以在更完整的查詢中結合,但先確認WHERE篩選的範圍一致。比如只統計已公開資料,卻從全部資料選最新名稱,兩個結果代表的集合就不同。這不是GROUP BY語法問題,而是統計範圍沒有對齊。
資料量大時再用EXPLAIN檢查實際查詢與索引;這張四筆資料的執行時間不能代表正式網站。也不要為了避免排序就退回任意取列。正確答案與效能調整是兩步:先讓每組選取規則可以說清楚,再評估符合這個規則的執行方式。
修復1055後,最後讀一遍欄位別名。greatest_name應表示最大字串,latest_name則必須來自你明確選出的最新列。名稱與資料意義一致,後續做報表、匯出或模板顯示的人才不會再把兩種結果混用。

評論0