<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          神奇的 SQL,Group By 真扎心,原來(lái)是這樣!

          共 5372字,需瀏覽 11分鐘

           ·

          2020-10-25 22:23

          GROUP BY 后 SELECT 列的限制

          標(biāo)準(zhǔn) SQL 規(guī)定,在對(duì)表進(jìn)行聚合查詢(xún)的時(shí)候,只能在 SELECT 子句中寫(xiě)下面 3 種內(nèi)容:通過(guò) GROUP BY 子句指定的聚合鍵、聚合函數(shù)(SUM 、AVG 等)、常量。我們來(lái)看個(gè)例子:
          我們有 學(xué)生班級(jí)表(tbl_student_class) 以及 數(shù)據(jù)如下 :


          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?'學(xué)號(hào)',
          ??cno?varchar(5)?NOT?NULL?COMMENT?'班級(jí)號(hào)',
          ??cname?varchar(20)?NOT?NULL?COMMENT?'班級(jí)名',
          ??PRIMARY?KEY?(id)
          )?COMMENT='學(xué)生班級(jí)表';

          --?----------------------------
          --?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班');


          我們想統(tǒng)計(jì)各個(gè)班(班級(jí)號(hào)、班級(jí)名)一個(gè)有多少人、以及最大的學(xué)號(hào),我們?cè)撛趺磳?xiě)這個(gè)查詢(xún) SQL ?我想大家應(yīng)該都會(huì)


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


          可是有人會(huì)想了,cno 和 cname 本來(lái)就是一對(duì)一,cno 一旦確定,cname 也就確定了,那 SQL 是不是可以這么寫(xiě) ?


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


          執(zhí)行報(bào)錯(cuò)了:
          [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 列表中的第二個(gè)表達(dá)式(cname)不在 GROUP BY 的子句中,同時(shí)它也不是聚合函數(shù);這與 sql 模式:ONLY_FULL_GROUP_BY 不相容。
          為什么 GROUP BY 之后不能直接引用原表(不在 GROUP BY 子句)中的列 ?莫急,我們慢慢往下看。


          SQL 模式

          MySQL 服務(wù)器可以在不同的 SQL 模式下運(yùn)行,并且可以針對(duì)不同的客戶(hù)端以不同的方式應(yīng)用這些模式,具體取決于 sql_mode 系統(tǒng)變量的值。DBA 可以設(shè)置全局SQL模式以匹配站點(diǎn)服務(wù)器操作要求,并且每個(gè)應(yīng)用程序可以將其會(huì)話 SQL 模式設(shè)置為其自己的要求。
          模式會(huì)影響 MySQL 支持的 SQL 語(yǔ)法以及它執(zhí)行的 數(shù)據(jù)驗(yàn)證檢查,這使得在不同環(huán)境中使用MySQL以及將MySQL與其他數(shù)據(jù)庫(kù)服務(wù)器一起使用變得更加容易。更多詳情請(qǐng)查閱官網(wǎng):Server SQL Modes。
          MySQL 版本不同,內(nèi)容會(huì)略有不同(包括默認(rèn)值),查閱的時(shí)候注意與自身的 MySQL 版本保持一致。
          SQL 模式主要分兩類(lèi):語(yǔ)法支持類(lèi)和數(shù)據(jù)檢查類(lèi),常用的如下


          語(yǔ)法支持類(lèi)    

          • ONLY_FULL_GROUP_BY


          對(duì)于 GROUP BY 聚合操作,如果在 SELECT 中的列、HAVING 或者 ORDER BY 子句的列,沒(méi)有在GROUP BY中出現(xiàn),那么這個(gè)SQL是不合法的


          • ANSI_QUOTES


          啟用 ANSI_QUOTES 后,不能用雙引號(hào)來(lái)引用字符串,因?yàn)樗唤忉尀樽R(shí)別符,作用與 ` 一樣。設(shè)置它以后,update t set f1="" …,會(huì)報(bào) Unknown column ‘’ in field list 這樣的語(yǔ)法錯(cuò)誤


          • PIPES_AS_CONCAT


          將 || 視為字符串的連接操作符而非 或 運(yùn)算符,這和Oracle數(shù)據(jù)庫(kù)是一樣的,也和字符串的拼接函數(shù) CONCAT() 相類(lèi)似


          • NO_TABLE_OPTIONS


          使用 SHOW CREATE TABLE 時(shí)不會(huì)輸出MySQL特有的語(yǔ)法部分,如 ENGINE ,這個(gè)在使用 mysqldump 跨DB種類(lèi)遷移的時(shí)候需要考慮


          • NO_AUTO_CREATE_USER


          字面意思不自動(dòng)創(chuàng)建用戶(hù)。在給MySQL用戶(hù)授權(quán)時(shí),我們習(xí)慣使用 GRANT … ON … TO dbuser 順道一起創(chuàng)建用戶(hù)。設(shè)置該選項(xiàng)后就與oracle操作類(lèi)似,授權(quán)之前必須先建立用戶(hù)


          數(shù)據(jù)檢查類(lèi)   

          • NO_ZERO_DATE


          認(rèn)為日期 ‘0000-00-00’ 非法,與是否設(shè)置后面的嚴(yán)格模式有關(guān)
          1、如果設(shè)置了嚴(yán)格模式,則 NO_ZERO_DATE 自然滿(mǎn)足。但如果是 INSERT IGNORE 或 UPDATE IGNORE,’0000-00-00’依然允許且只顯示warning;
          2、如果在非嚴(yán)格模式下,設(shè)置了NO_ZERO_DATE,效果與上面一樣,’0000-00-00’ 允許但顯示warning;如果沒(méi)有設(shè)置NO_ZERO_DATE,no warning,當(dāng)做完全合法的值;
          3、NO_ZERO_IN_DATE情況與上面類(lèi)似,不同的是控制日期和天,是否可為 0 ,即 2010-01-00 是否合法;


          • NO_ENGINE_SUBSTITUTION


          使用 ALTER TABLE 或 CREATE TABLE 指定 ENGINE 時(shí), 需要的存儲(chǔ)引擎被禁用或未編譯,該如何處理。啟用 NO_ENGINE_SUBSTITUTION 時(shí),那么直接拋出錯(cuò)誤;不設(shè)置此值時(shí),CREATE用默認(rèn)的存儲(chǔ)引擎替代,ATLER不進(jìn)行更改,并拋出一個(gè) warning


          • STRICT_TRANS_TABLES


          設(shè)置它,表示啟用嚴(yán)格模式。注意 STRICT_TRANS_TABLES 不是幾種策略的組合,單獨(dú)指 INSERT、UPDATE 出現(xiàn)少值或無(wú)效值該如何處理:
          1、前面提到的把 ‘’ 傳給int,嚴(yán)格模式下非法,若啟用非嚴(yán)格模式則變成 0,產(chǎn)生一個(gè)warning;
          2、Out Of Range,變成插入最大邊界值;
          3、當(dāng)要插入的新行中,不包含其定義中沒(méi)有顯式DEFAULT子句的非NULL列的值時(shí),該列缺少值;


          默認(rèn)模式

          當(dāng)我們沒(méi)有修改配置文件的情況下,MySQL 是有自己的默認(rèn)模式的;版本不同,默認(rèn)模式也不同


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

          --?查看?sql_mode
          SELECT?@@sql_mode;


          我們可以看到,5.7.21 的默認(rèn)模式包含:


          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


          而第一個(gè):ONLY_FULL_GROUP_BY 就會(huì)約束:當(dāng)我們進(jìn)行聚合查詢(xún)的時(shí)候,SELECT 的列不能直接包含非 GROUP BY 子句中的列。那如果我們?nèi)サ粼撃J剑◤摹皣?yán)格模式”到“寬松模式”)呢 ?
          我們發(fā)現(xiàn),上述報(bào)錯(cuò)的 SQL


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


          能正常執(zhí)行了,但是一般情況下不推薦這樣配置,線上環(huán)境往往是“嚴(yán)格模式”,而不是“寬松模式”;雖然案例中,無(wú)論是“嚴(yán)格模式”,還是“寬松模式”,結(jié)果都是對(duì)的,那是因?yàn)?cno 與 cname 唯一對(duì)應(yīng)的,如果 cno 與 cname 不是唯一對(duì)應(yīng),那么在“寬松模式下” cname 的值是隨機(jī)的,這就會(huì)造成難以排查的問(wèn)題,有興趣的可以去試試。那為什么會(huì)有 ONLY_FULL_GROUP_BY 模式呢 ??我們繼續(xù)往下看:

          階(order)是用來(lái)區(qū)分集合或謂詞的階數(shù)的概念。謂詞邏輯中,根據(jù)輸入值的階數(shù)對(duì)謂詞進(jìn)行分類(lèi)。
          = 或者 BETWEEEN 等輸入值為一行的謂詞叫作"一階謂詞",而像 EXISTS 這樣輸入值為行的集合的謂詞叫作"二階謂詞"(HAVING 的輸入值也是集合,但它不是謂詞)。以此類(lèi)推,三階謂詞=輸入值為"集合的集合"的謂詞,四階謂詞=輸入值為"集合的集合的集合"的謂詞,但是 SQL 里并不會(huì)出現(xiàn)三階以上的情況,所以不用太在意。
          簡(jiǎn)單點(diǎn)如下圖
          談到了階,就不得不談下集合論;集合論是 SQL 語(yǔ)言的根基,因?yàn)樗倪@個(gè)特性,SQL 也被稱(chēng)為面向集合語(yǔ)言。只有從集合的角度來(lái)思考,才能明白 SQL 的強(qiáng)大威力。通過(guò)上圖,相信大家也都能看到,這里不做更深入的講解了,有興趣的可以去查相關(guān)資料。


          為什么聚合后不能再引用原表中的列

          很多人都知道聚合查詢(xún)的限制,但是很少有人能正確地理解為什么會(huì)有這樣的約束。表 tbl_student_class 中的 cname 存儲(chǔ)的是每位學(xué)生的班級(jí)信息。
          但需要注意的是,這里的 cname 只是每個(gè)學(xué)生的屬性,并不是小組的屬性,而 GROUP BY 又是聚合操作,操作的對(duì)象就是由多個(gè)學(xué)生組成的小組,因此,小組的屬性只能是平均或者總和等統(tǒng)計(jì)性質(zhì)的屬性,如下圖
          詢(xún)問(wèn)每個(gè)學(xué)生的 cname 是可以的,但是詢(xún)問(wèn)由多個(gè)學(xué)生組成的小組的 cname 就沒(méi)有意義了。對(duì)于小組來(lái)說(shuō),只有"一共多少學(xué)生"或者"最大學(xué)號(hào)是多少?"這樣的問(wèn)法才是有意義的。
          強(qiáng)行將適用于個(gè)體的屬性套用于團(tuán)體之上,純粹是一種分類(lèi)錯(cuò)誤;而 GROUP BY 的作用是將一個(gè)個(gè)元素劃分成若干個(gè)子集,使用 GROUP BY 聚合之后,SQL 的操作對(duì)象便由 0 階的"行"變?yōu)榱?1 階的"行的集合",此時(shí),行的屬性便不能使用了。
          SQL 的世界其實(shí)是層級(jí)分明的等級(jí)社會(huì),將低階概念的屬性用在高階概念上會(huì)導(dǎo)致秩序的混亂,這是不允許的。此時(shí)我相信大家都明白:為什么聚合后不能再引用原表中的列 。

          單元素集合也是集合

          現(xiàn)在的集合論認(rèn)為單元素集合是一種正常的集合。單元素集合和空集一樣,主要是為了保持理論的完整性而定義的。因此對(duì)于以集合論為基礎(chǔ)的 SQL 來(lái)說(shuō),當(dāng)然也需要嚴(yán)格地區(qū)分元素和單元素集合。因此,元素 a 和集合 {a} 之間存在著非常醒目的層級(jí)差別。
          a?≠?{a}
          這兩個(gè)層級(jí)的區(qū)別分別對(duì)應(yīng)著 SQL 中的 WHERE 子句和 HAVING 子句的區(qū)別。WHERE 子句用于處理"行"這種 0 階的對(duì)象,而 HAVING 子句用來(lái)處理"集合"這種 1 階的對(duì)象。

          總結(jié)

          1、SQL 嚴(yán)格區(qū)分層級(jí),包括謂詞邏輯中的層級(jí)(EXISTS),也包括集合論中的層級(jí)(GROUP BY);
          2、有了層級(jí)區(qū)分,那么適用于個(gè)體上的屬性就不適用于團(tuán)體了,這也就是為什么聚合查詢(xún)的 SELECT 子句中不能直接引用原表中的列的原因;
          3、一般來(lái)說(shuō),單元素集合的屬性和其唯一元素的屬性是一樣的。這種只包含一個(gè)元素的集合讓人覺(jué)得似乎沒(méi)有必要特意地當(dāng)成集合來(lái)看待,但是為了保持理論的完整性,我們還是要嚴(yán)格區(qū)分元素和單元素集合;

          參考

          《SQL基礎(chǔ)教程》
          《SQL進(jìn)階教程》

          作者:青石路

          原文:cnblogs.com/youzhibing/p/11516154.html

          瀏覽 56
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.大鸡巴免费99 | 黄片免费观看永久免费 | 欧美三级电影网站 | 懂色av蜜臀av粉嫩av分享 | 人人看人人射 |