為什麼我的 GROUP BY 分組又報錯了?

2021-03-02 數據不吹牛

點擊關註上方「數據不吹牛」,

或設為「置頂或星標」,第一時間送達乾貨


大家好,我是小zSQL可以說是分析師的立命之本了(之一),但經常有同學還是會在一些看似簡單的問題上翻車。今天分享的這篇文章,就來深入淺出地講講最常見的GROUP BY 函數不常見的一面。GROUP BY 後 SELECT 列的限制
標準 SQL 規定,在對表進行聚合查詢的時候,只能在 SELECT 子句中寫下面 3 種內容:通過 GROUP BY 子句指定的聚合鍵、聚合函數(SUM 、AVG 等)、常量。我們來看個例子:我們有 學生班級表(tbl_student_class) 以及 數據如下 :

DROP TABLE IF EXISTS tbl_student_class;
CREATE TABLE tbl_student_class (
  id int(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
  sno varchar(12) NOT NULL COMMENT '學號',
  cno varchar(5) NOT NULL COMMENT '班級號',
  cname varchar(20) NOT NULL COMMENT '班級名',
  PRIMARY KEY (id)
) COMMENT='學生班級表';

-- ---
-- Records of tbl_student_class
-- ---
INSERT INTO tbl_student_class VALUES ('1', '20190607001', '0607', '影視7班');
INSERT INTO tbl_student_class VALUES ('2', '20190607002', '0607', '影視7班');
INSERT INTO tbl_student_class VALUES ('3', '20190608003', '0608', '影視8班');
INSERT INTO tbl_student_class VALUES ('4', '20190608004', '0608', '影視8班');
INSERT INTO tbl_student_class VALUES ('5', '20190609005', '0609', '影視9班');
INSERT INTO tbl_student_class VALUES ('6', '20190609006', '0609', '影視9班');

我們想統計各個班(班級號、班級名)一個有多少人、以及最大的學號,我們該怎麼寫這個查詢 SQL ?我想大家應該都會

SELECT cno,cname,count(sno),MAX(sno) 
FROM tbl_student_class
GROUP BY cno,cname;

可是有人會想了,cno 和 cname 本來就是一對一,cno 一旦確定,cname 也就確定了,那 SQL 是不是可以這麼寫 ?

SELECT cno,cname,count(sno),MAX(sno) 
FROM tbl_student_class
GROUP BY cno;

[Err] 1055 - Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.tbl_student_class.cname' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

提示信息:SELECT 列表中的第二個表達式(cname)不在 GROUP BY 的子句中,同時它也不是聚合函數;這與 sql 模式:ONLY_FULL_GROUP_BY 不相容。為什麼 GROUP BY 之後不能直接引用原表(不在 GROUP BY 子句)中的列 ?莫急,我們慢慢往下看。SQL 模式MySQL 伺服器可以在不同的 SQL 模式下運行,並且可以針對不同的客戶端以不同的方式應用這些模式,具體取決於 sql_mode 系統變量的值。DBA 可以設置全局SQL模式以匹配站點伺服器操作要求,並且每個應用程式可以將其會話 SQL 模式設置為其自己的要求。模式會影響 MySQL 支持的 SQL 語法以及它執行的 數據驗證檢查,這使得在不同環境中使用MySQL以及將MySQL與其他資料庫伺服器一起使用變得更加容易。更多詳情請查閱官網:Server SQL Modes。MySQL 版本不同,內容會略有不同(包括默認值),查閱的時候注意與自身的 MySQL 版本保持一致。SQL 模式主要分兩類:語法支持類和數據檢查類,常用的如下語法支持類    對於 GROUP BY 聚合操作,如果在 SELECT 中的列、HAVING 或者 ORDER BY 子句的列,沒有在GROUP BY中出現,那麼這個SQL是不合法的啟用 ANSI_QUOTES 後,不能用雙引號來引用字符串,因為它被解釋為識別符,作用與 ` 一樣。設置它以後,update t set f1="" …,會報 Unknown column 『』 in field list 這樣的語法錯誤將 || 視為字符串的連接操作符而非 或 運算符,這和Oracle資料庫是一樣的,也和字符串的拼接函數 CONCAT() 相類似使用 SHOW CREATE TABLE 時不會輸出MySQL特有的語法部分,如 ENGINE ,這個在使用 mysqldump 跨DB種類遷移的時候需要考慮字面意思不自動創建用戶。在給MySQL用戶授權時,我們習慣使用 GRANT … ON … TO dbuser 順道一起創建用戶。設置該選項後就與oracle操作類似,授權之前必須先建立用戶數據檢查類   認為日期 『0000-00-00』 非法,與是否設置後面的嚴格模式有關1、如果設置了嚴格模式,則 NO_ZERO_DATE 自然滿足。但如果是 INSERT IGNORE 或 UPDATE IGNORE,』0000-00-00』依然允許且只顯示warning;2、如果在非嚴格模式下,設置了NO_ZERO_DATE,效果與上面一樣,』0000-00-00』 允許但顯示warning;如果沒有設置NO_ZERO_DATE,no warning,當做完全合法的值;3、NO_ZERO_IN_DATE情況與上面類似,不同的是控制日期和天,是否可為 0 ,即 2010-01-00 是否合法;使用 ALTER TABLE 或 CREATE TABLE 指定 ENGINE 時, 需要的存儲引擎被禁用或未編譯,該如何處理。啟用 NO_ENGINE_SUBSTITUTION 時,那麼直接拋出錯誤;不設置此值時,CREATE用默認的存儲引擎替代,ATLER不進行更改,並拋出一個 warning設置它,表示啟用嚴格模式。注意 STRICT_TRANS_TABLES 不是幾種策略的組合,單獨指 INSERT、UPDATE 出現少值或無效值該如何處理:
1、前面提到的把 『』 傳給int,嚴格模式下非法,若啟用非嚴格模式則變成 0,產生一個warning;2、Out Of Range,變成插入最大邊界值;3、當要插入的新行中,不包含其定義中沒有顯式DEFAULT子句的非NULL列的值時,該列缺少值;默認模式當我們沒有修改配置文件的情況下,MySQL 是有自己的默認模式的;版本不同,默認模式也不同

-- 查看 MySQL 版本
SELECT VERSION();

-- 查看 sql_mode
SELECT @@sql_mode;

我們可以看到,5.7.21 的默認模式包含:

ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

而第一個:ONLY_FULL_GROUP_BY 就會約束:當我們進行聚合查詢的時候,SELECT 的列不能直接包含非 GROUP BY 子句中的列。那如果我們去掉該模式(從「嚴格模式」到「寬鬆模式」)呢 ?

我們發現,上述報錯的 SQL

-- 寬鬆模式下 可以執行
SELECT cno,cname,count(sno),MAX(sno) 
FROM tbl_student_class
GROUP BY cno;

能正常執行了,但是一般情況下不推薦這樣配置,線上環境往往是「嚴格模式」,而不是「寬鬆模式」;雖然案例中,無論是「嚴格模式」,還是「寬鬆模式」,結果都是對的,那是因為 cno 與 cname 唯一對應的,如果 cno 與 cname 不是唯一對應,那麼在「寬鬆模式下」 cname 的值是隨機的,這就會造成難以排查的問題,有興趣的可以去試試。那為什麼會有 ONLY_FULL_GROUP_BY 模式呢 ? 我們繼續往下看階(order)是用來區分集合或謂詞的階數的概念。謂詞邏輯中,根據輸入值的階數對謂詞進行分類。= 或者 BETWEEEN 等輸入值為一行的謂詞叫作"一階謂詞",而像 EXISTS 這樣輸入值為行的集合的謂詞叫作"二階謂詞"(HAVING 的輸入值也是集合,但它不是謂詞)。以此類推,三階謂詞=輸入值為"集合的集合"的謂詞,四階謂詞=輸入值為"集合的集合的集合"的謂詞,但是 SQL 裡並不會出現三階以上的情況,所以不用太在意。在公眾號SQL資料庫開發中回復1024,送你一整套學習資料。

談到了階,就不得不談下集合論;集合論是 SQL 語言的根基,因為它的這個特性,SQL 也被稱為面向集合語言。只有從集合的角度來思考,才能明白 SQL 的強大威力。通過上圖,相信大家也都能看到,這裡不做更深入的講解了,有興趣的可以去查相關資料。為什麼聚合後不能再引用原表中的列很多人都知道聚合查詢的限制,但是很少有人能正確地理解為什麼會有這樣的約束。表 tbl_student_class 中的 cname 存儲的是每位學生的班級信息。但需要注意的是,這裡的 cname 只是每個學生的屬性,並不是小組的屬性,而 GROUP BY 又是聚合操作,操作的對象就是由多個學生組成的小組,因此,小組的屬性只能是平均或者總和等統計性質的屬性,如下圖

詢問每個學生的 cname 是可以的,但是詢問由多個學生組成的小組的 cname 就沒有意義了。對於小組來說,只有"一共多少學生"或者"最大學號是多少?"這樣的問法才是有意義的。強行將適用於個體的屬性套用於團體之上,純粹是一種分類錯誤;而 GROUP BY 的作用是將一個個元素劃分成若干個子集,使用 GROUP BY 聚合之後,SQL 的操作對象便由 0 階的"行"變為了 1 階的"行的集合",此時,行的屬性便不能使用了。SQL 的世界其實是層級分明的等級社會,將低階概念的屬性用在高階概念上會導致秩序的混亂,這是不允許的。此時我相信大家都明白:為什麼聚合後不能再引用原表中的列 。單元素集合也是集合現在的集合論認為單元素集合是一種正常的集合。單元素集合和空集一樣,主要是為了保持理論的完整性而定義的。因此對於以集合論為基礎的 SQL 來說,當然也需要嚴格地區分元素和單元素集合。因此,元素 a 和集合 {a} 之間存在著非常醒目的層級差別。

a ≠ {a}

這兩個層級的區別分別對應著 SQL 中的 WHERE 子句和 HAVING 子句的區別。WHERE 子句用於處理"行"這種 0 階的對象,而 HAVING 子句用來處理"集合"這種 1 階的對象。總結1、SQL 嚴格區分層級,包括謂詞邏輯中的層級(EXISTS),也包括集合論中的層級(GROUP BY);2、有了層級區分,那麼適用於個體上的屬性就不適用於團體了,這也就是為什麼聚合查詢的 SELECT 子句中不能直接引用原表中的列的原因;3、一般來說,單元素集合的屬性和其唯一元素的屬性是一樣的。這種只包含一個元素的集合讓人覺得似乎沒有必要特意地當成集合來看待,但是為了保持理論的完整性,我們還是要嚴格區分元素和單元素集合;參考

《SQL基礎教程》
《SQL進階教程》

作者:青石路

cnblogs.com/youzhibing/p/11516154.html

相關焦點

  • Oracle分組查詢group by的用法及講解
    group by是sql中比較強大的功能,是在對數據分組統計時必不可少的用法。但是,對於很多經驗不足的同學,經常會寫錯。今天我們就以Oracle為例,來講解下分組查詢group by的用法。原因是group by 分組查詢,select子句後的欄位必須來自group by後的分組欄位。於是 我們執行SQLSELECT SSEX FROM STUDENT GROUP BY SSEX;這下成功地將數據分為了兩組。
  • sql 分組語句group by
    聚合函數如sum(),max()等,通常需要結合分組語句group by 使用GROUP BY 語句用於結合合計函數,根據一個或多個列對結果集進行分組
  • Mysql分組查詢group by使用示例
    (1) group by的含義:將查詢結果按照1個或多個欄位進行分組,欄位值相同的為一組(2) group by可用於單個欄位分組
  • 聊聊mysql分組查詢group by以及分組條件having的用法
    今天和大家一起學習一下mysql的分組查詢group by的使用方法,也是重新回憶和複習一下。我們來看一下分組查詢的語法:1、語法:group by + 分組的欄位;下面我們來看一張學生表信息:我們現在需要實現這樣一個需求:1、按性別分組,分別查詢出男、女學生的數學平均分
  • SQL分組函數和聚合函數的幾點說明
    拿上圖中的數據進行解釋,假設按照product_type這個欄位進行分組,分組之後結果如下圖。SELECT DISTINCT product_type from product假設分組之後,我想看一下價格,也就是sale_price這個欄位的值,按照如下這個寫法,會報如下錯誤。
  • MySQL資料庫的分組操作,語句拼接,列轉行操作
    大家好,我是anyux。本文介紹MySQL資料庫的分組操作,語句拼接,列轉行操作。關於group by 的sql_mode關於group by 的sql_modeonly_full_group_by>示例group by 錯誤select user,host from mysql.user group by user;報錯內容ERROR 1055 (42000): Expression 2 of SELECT list
  • 分組查詢時,select的欄位是否一定要都在group by中?
    分組查詢關鍵字group by通常和集合函數(MAX、MIN、COUNT、SUM、AVG)一起使用,它可以對一列或者多列結果集進行分組。
  • LeetCode-49.字母異位詞分組(Group Anagrams)
    字母異位詞分組給定一個字符串數組,將字母異位詞組合在一起。字母異位詞指字母相同,但排列不同的字符串。來源:力扣(LeetCode)連結:https://leetcode-cn.com/problems/group-anagrams/Link:https://leetcode.com/problems/group-anagrams/排序+哈希O(N * KlogK), N等於單詞個數,K等於單詞平均長度
  • NET開發-在C#的LINQ查詢語句中,如何使用group……by……into分組
    在此代碼中,是按Sex欄位的值進行分組,使用s.Key表示分組的鍵,可以循環輸出Sex的值。案例一:使用Group…by分組輸出對C#泛型列表List<Student>集合中的Sex進行分組,並輸出分組欄位Sex的值,然後再輸出每個分組結果,C#代碼如下:List<Student> StudentList1 = new List<Student&
  • Java從零開始學 - 第64篇:分組查詢詳解(group by & having)
    本篇內容 分組查詢語法聚合函數單欄位分組多欄位分組分組前篩選數據分組後篩選數據where和having的區別分組後排序where & group by & having & order by & limit 一起協作mysql分組中的坑
  • 【資料庫】group by的用法實例
    如圖有這樣一張成績表:首先要理解group by 含義:「Group By」從字面意義上理解就是根據「By」指定的規則對數據進行分組,所謂的分組就是將一個「數據集」劃分成若干個「小區域」,然後針對若干個「小區域」進行數據處理。
  • GroupFace
    Cvpr2020的這一篇group face別出心裁,採用改進網絡結構定義的方式來提升了識別的精度,並且在各大數據集上刷新了記錄。首先來說一下group face最核心的貢獻點在哪裡?通過改進網絡結構的方式,引進特徵分組並且學習特徵分組,本質就是引入了額外的監督信息。
  • MySQL最常用分組聚合函數
    group by子句  根據給定列或者表達式的每一個不同的值將表中的行分成不同的組,使用組函數返回每一組的統計信息規則:  ①出現在SELECT子句中的單獨的列,必須出現在GROUP BY子句中作為分組列  ②分組列可以不出現在SELECT子句中  ③分組列可出現在SELECT子句中的一個複合表達式中  ④如果GROUP
  • Mysql資料庫的group by子句
    形式:group by 欄位名1 【Asc | Desc】, 欄位名2 【Asc | Desc】,......說明:1,用於將從數據源中取得的滿足where條件的數據,進行某種標準(欄位)的分類(分組);2,分組之後,要有一個重要理念:select出來的數據,每一行,就是代表「一組」!!!即該行的每一個欄位的值,都應該是對該「組」的信息描述!!!
  • 黑客日教程-Java8新功能:將數據集合分組,類似SQL的GROUP BY
    這兩個方法將數據按某些屬性分組,並存儲在Map中返回。 下面是幾個重載的groupnigBy方法:- 參數:分類函數 ```static <T,K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<?
  • 玩轉 Pandas 的 Groupby 操作
    玩轉 Pandas 的 Groupby 操作大家好,我是 Lemon,今天來跟大家分享下 pandas 中 groupby 的用法。Pandas 的 groupby() 功能很強大,用好了可以方便的解決很多問題,在數據處理以及日常工作中經常能施展拳腳。
  • pandas系列(三)Pandas分組
    分組函數的基本內容:(a)根據某一列分組grouped_single = df.groupby('School')經過groupby後會生成一個groupby對象,該對象本身不會返回任何東西,只有當相應的方法被調用才會起作用
  • select學習第六天——GROUP BY 多條件求和(SQL)
    group by 多條件求和2018年12月29日,學習SQL第六天了,好激動好激動,原來看都不敢看的內容,想不到已經學了6天了,感覺越來越難了,那就做一隻烏龜,一點一點學,一點一點琢磨,總會在某一時刻會感謝現在學習的自己!
  • 分組?原來你是玩兒真的!
    數據計算中,分組絕對是最常用的計算方式之一,相應的,在SQL語言中,自然會用到group by了。但是,不知道你想過沒有,SQL中的group by並不能算是「純粹」的分組,它實際上並不是針對原始數據分組,而是將結果集分組,最終是為了實現5類聚合計算:min/max/avg/count/sum,而單獨使用group by沒有任何意義,只是相當於按照group by的條件進行了排序而已。但實際的數據計算中,針對分組數據的統計要求有可能遠遠超過5類聚合計算的能力。
  • ​SQL數據分析GROUP BY語句這樣用
    那如果你要按性別gender分組統計學生數,這個時候就要用到group by了,SQL語句如下SELECT gender,count(*) FROM usergroup by gender按什麼分組,就直接在group by後面加上對應的分組欄位,同時,SELECT後面、聚合函數前面也要加上對應的分組欄位,這樣才能正常顯示。