
你真的會(huì)玩SQL嗎?之邏輯查詢處理階段
最近要對(duì)數(shù)據(jù)庫(kù)進(jìn)行優(yōu)化,但由于工作項(xiàng)目中已經(jīng)很少親自寫SQL而且用的都不是很復(fù)雜的語(yǔ)句,所以有些生疏了,于是翻翻N年前的筆記資料,想以此來(lái)記錄回顧總結(jié)一些實(shí)用的SQL干貨讓大家來(lái)學(xué)習(xí),若有不對(duì)之處可提出。
記得剛出來(lái)行走江湖的時(shí)候也是只會(huì)增、刪、改、查四大法寶,一般公司沒有多少?gòu)?fù)雜的業(yè)務(wù),所以就夠用了。但后來(lái)看著大神會(huì)寫個(gè)幾百行的SQL存儲(chǔ)過程就感覺自己是不是弱爆了。
如今是大數(shù)據(jù)的時(shí)代,對(duì)數(shù)據(jù)的處理要求越來(lái)越重視,要出各種數(shù)據(jù)報(bào)表,因此百萬(wàn)數(shù)據(jù)處理速度,數(shù)據(jù)庫(kù)明顯比后臺(tái)邏輯處理的優(yōu)勢(shì)不是一個(gè)別。
下面進(jìn)入正題,寫了多年的SQL,你真的玩會(huì)了SQL嗎?
在此我想再次提示一個(gè)數(shù)據(jù)處理的中心思想,SQL數(shù)據(jù)處理是集合思維,不要用邏輯思維來(lái)思考。
文中的示例來(lái)自自己的積累和TSQL2008技術(shù)內(nèi)幕。
基礎(chǔ)知識(shí)普及
對(duì)于教條式的定義請(qǐng)自己去查,此處不會(huì)涉及到文鄒鄒的知識(shí),但還是強(qiáng)調(diào)一下基礎(chǔ)的重要性,即使你理解了所有的概念,但當(dāng)組合起來(lái)用時(shí)也會(huì)一頭霧水。
邏輯查詢處理階段
在以上的10個(gè)處理步驟中, 每一步的處理都生成一個(gè)虛擬表來(lái)作為下一步的輸入. 虛擬表對(duì)于調(diào)用者或輸出查詢來(lái)說(shuō)是不存在的, 僅在最后步驟生成的表才會(huì)返回給調(diào)用者或者輸出查詢. 如果某一子句沒有出現(xiàn)在SQL語(yǔ)句中, 這一步就被簡(jiǎn)單跳過..
這10個(gè)具體步驟是:
1.FROM: from子句中的兩個(gè)表首先進(jìn)行交叉連接(笛卡爾積), 生成虛擬表VT1。
2.ON:
on條件作用在VT1上, 將條件為True的行生成VT2。
3.OUTER: 如果outer join被指定, 則根據(jù)外連接條件,
將左表or右表or多表的未出現(xiàn)在VT2查詢結(jié)果中的行加入到VT2后生成VT3。
4.WHERE: VT3表中應(yīng)用Where條件,
結(jié)果為真的行用來(lái)生成VT4。
5.GROUP BY: 根據(jù)Group by指定的列, 將VT4的行組織到不同的組中,
生成VT5。
6.CLUB|ROLLUP: 超級(jí)組(分組之后的分組)被添加到VT5中, 生成VT6。
7.HAVING: Having用來(lái)篩選組,
VT6上符合條件的組將用來(lái)生成VT7。
8.SELECT: select子句用來(lái)選擇指定的列, 并生成VT8。
9.DISTINCT:
從VT8中刪除重復(fù)的行后, VT9被生成。
10.ORDER BY: 根據(jù)Order by子句, VT9中的行被排序, 生成游標(biāo)10。
注意事項(xiàng):
第一步中FROM: 需要對(duì)兩表同時(shí)存在的列添加前綴, 以免混淆.
第二步中ON: 在SQL特有的三值邏輯(true,false,unknown)中, unkown的值也是確定的, 只是在不同情況下有時(shí)為true, 有時(shí)為false. 一個(gè)總的原則是: unknown的值非真即假, 非假即真. 也就是時(shí)說(shuō), unknown只能取true和false里面的一個(gè)值, 但是unknown的相反還是unknown.如:
在ON、WHERE和HAVING中做過濾條件時(shí), unknown看做false;
在CHECK約束中, unknown被看做是true;
在條件中, 兩個(gè)NULL的比較結(jié)果還是Unknown.
在UNIQUE和PRIMARY KEY約束、排序和分組中, NULL被看做是相等的. 例如Group by 將null分為一組, 而order by將所有null排在一起.
第三步中OUTER: 如果多余兩張表, 則將VT3和FROM中的下一張表再次執(zhí)行從第一步到第三步的過程.
第四步中WHERE: 由于此刻沒有分組, 也沒有執(zhí)行select所以, where子句中不能寫分組函數(shù), 也不能使用表的別名. 并且, 只有在外連接時(shí), on和where的邏輯才是不同的, 因此建議連接條件放在on中.
第五步中GROUP BY: 如果查詢中包含Group by 子句, 那么所有的后續(xù)操作(having, select等)都是對(duì)每一組的結(jié)果進(jìn)行操作.
Group by子句中可以使用組函數(shù), 在Sql 2000中一旦使用組函數(shù), 其后面的步驟將都不能處理, 而在
Sql2005中沒有這個(gè)限制.
第六步不常用, 略過.
第七步中HAVING: having表達(dá)式是僅有的分組條件. 注意: count(*)不會(huì)忽略掉null, 而count(field)會(huì); 此外分組函數(shù)中不支持子查詢做輸入.
第八步中SELECT: 如果包含Group By子句, 那么在第5步后將只能使用Group By子句中出現(xiàn)的列, 如果要使用其他原始列則, 只能使用組函數(shù).
另外, select在第八步才執(zhí)行, 因此別名只能第八步之后才能使用, 并且只能在order by中使用.
第九步中DISTINCT: 當(dāng)使用Group By子句時(shí), 使用Distinct是多余的, 他不會(huì)刪除任何記錄.
第十步中ORDER BY: 按Order by子句指定的列排序后, 返回游標(biāo)VC10.
別名只能在Order by子句中使用.
如果定義了Distinct子句, 則只能排序上一步中返回的表VT9, 如果沒有指定Distinct子句, 則可以排序不再最終結(jié)果集中的列. 例如: 如果不加Distinct則Order by可以訪問VT7和VT8中的內(nèi)容.
這一步最不同的是它返回的是游標(biāo)而不是表, Sql是基于集合論的, 集合中的元素師沒有順序的, 一個(gè)在表上引用Order by排序的查詢返回一個(gè)按照特定特定物理順序組織的對(duì)象—游標(biāo). 所以對(duì)于視圖、子查詢、派生表等均不能將order by結(jié)果作為其數(shù)據(jù)來(lái)源.
建議: 使用表的表達(dá)式時(shí), 不允許使用order by子句的查詢, 因此除非你真的要對(duì)行排序, 否則不要使用order by 子句.
內(nèi)容為 RJ 寫的,邏輯非常清楚,值得花點(diǎn)時(shí)間理解,再次強(qiáng)調(diào)是因?yàn)閺?fù)雜的集合數(shù)據(jù)處理過程中會(huì)得到不是你想要的結(jié)果,這時(shí)就要你自己腦袋當(dāng)SQL處理器來(lái)推出結(jié)果查出問題,可能大多數(shù)寫了幾年的SQL都還沒弄明白,但到了用時(shí)還是提前理解下,非常重要。
練習(xí)
此后用到的用例數(shù)據(jù)庫(kù)是SQL2008里面的
/*1.返回來(lái)自美國(guó)的客戶,并為每個(gè)客戶返回其訂單總數(shù)和商品交易總數(shù)量。
涉及到表:Sales.Customers表、Sales.Orders表,以及Sales.OrderDetails表。
期望的輸出:
*/
custid numorders totalqty
----------- ----------- -----------
32 11 345
36 5 122
43 2 20
45 4 181
48 8 134
55 10 603
65 18 1383
71 31 4958
75 9 327
77 4 46
78 3 59
82 3 89
89 14 1063
1參考SQL:
--answer:
select c.custid,count(distinct o.orderid) as 'numorders',sum(od.qty) as 'totalqty'
from Sales.Customers as c
join Sales.Orders as o
on c.custid=o.custid
join Sales.OrderDetails as od
on o.orderid=od.orderid
where c.country='USA'
group by c.custid
/*
1.將表Sales.Customers別名為c和表Sales.Orders別名為o應(yīng)用ON篩選器以custid為條件內(nèi)連接,生成虛擬表VT1,
2.將虛擬表VT1和表Sales.OrderDetails應(yīng)用ON篩選器以orderid為條件內(nèi)連接,生成虛擬表VT2,
3.對(duì)上一步返回的虛擬表中的所有行應(yīng)用where篩選器返回滿足條件c.country='USA'的虛擬表VT3,
4.應(yīng)用group by子句將數(shù)據(jù)以c.custid列分組
5.處理select列表,去掉重復(fù)o.orderid再用count統(tǒng)計(jì)個(gè)數(shù)返回別名為numorders的列,統(tǒng)計(jì)od.qty列別名totalqty
*/
/*2:返回客戶及其訂單信息,包括沒有下過任何訂單的客戶。
涉及到表:Sales.Customers和Sales.Orders表。
期望的輸出(按簡(jiǎn)略的格式顯示):
*/
custid companyname orderid orderdate
----------- --------------- ----------- ------------------------
85 Customer ENQZT 10248 2006-07-04 00:00:00.000
79 Customer FAPSM 10249 2006-07-05 00:00:00.000
34 Customer IBVRG 10250 2006-07-08 00:00:00.000
84 Customer NRCSK 10251 2006-07-08 00:00:00.000
...
73 Customer JMIKW 11074 2008-05-06 00:00:00.000
68 Customer CCKOT 11075 2008-05-06 00:00:00.000
9 Customer RTXGC 11076 2008-05-06 00:00:00.000
65 Customer NYUHS 11077 2008-05-06 00:00:00.000
22 Customer DTDMN NULL NULL
57 Customer WVAXS NULL NULL
2參考SQL:
--answer:
select c.custid,c.companyname,o.orderid,o.orderdate
from Sales.Customers as c
left join Sales.Orders as o
on c.custid=o.custid
/*
1.將表Sales.Customers別名為c和表Sales.Orders別名為o應(yīng)用ON篩選器以custid為條件左外連接,生成虛擬表VT1,
2.添加外部行,外部行中非保留表中的屬性被賦值為NULL,生成虛擬表VT2
3.處理select列表,查找出c.custid,c.companyname,o.orderid,o.orderdate生成虛擬表VT3
*/
/*3:返回值2007年2月12日下過訂單的客戶,以及他們的訂單。同時(shí)也返回在2007年2月12日沒有下過訂單的客戶。
涉及到表:Sales.Customers表和Sales.Orders表。
期望的輸出(按簡(jiǎn)略格式顯示):
*/
custid companyname orderid orderdate
----------- --------------- ----------- -----------------------
72 Customer AHPOP NULL NULL
58 Customer AHXHT NULL NULL
25 Customer AZJED NULL NULL
18 Customer BSVAR NULL NULL
91 Customer CCFIZ NULL NULL
...
33 Customer FVXPQ NULL NULL
53 Customer GCJSG NULL NULL
39 Customer GLLAG NULL NULL
16 Customer GYBBY NULL NULL
4 Customer HFBZG NULL NULL
5 Customer HGVLZ 10444 2007-02-12 00:00:00.000
42 Customer IAIJK NULL NULL
34 Customer IBVRG NULL NULL
63 Customer IRRVL NULL NULL
73 Customer JMIKW NULL NULL
15 Customer JUWXK NULL NULL
...
21 Customer KIDPX NULL NULL
30 Customer KSLQF NULL NULL
55 Customer KZQZT NULL NULL
71 Customer LCOUJ NULL NULL
77 Customer LCYBZ NULL NULL
66 Customer LHANT 10443 2007-02-12 00:00:00.000
38 Customer LJUCA NULL NULL
59 Customer LOLJO NULL NULL
36 Customer LVJSO NULL NULL
64 Customer LWGMD NULL NULL
29 Customer MDLWA NULL NULL
...
3參考SQL:
--answer:
select c.custid,c.companyname,o.orderid,o.orderdate
from Sales.Customers as c
left join Sales.Orders as o
on c.custid=o.custid
and o.orderdate='2007-2-12'
/*
1.將表Sales.Customers別名為c和表Sales.Orders別名為o應(yīng)用ON篩選器以custid和o.orderdate='2007-2-12'為條件左外連接,生成虛擬表VT1,
2.添加外部行,外部行中非保留表中的屬性被賦值為NULL,生成虛擬表VT2
3.處理select列表,從虛擬表VT2中查找出c.custid,c.companyname,o.orderid,o.orderdate生成虛擬表VT3
*/
數(shù)據(jù)分析咨詢請(qǐng)掃描二維碼
若不方便掃碼,搜微信號(hào):CDAshujufenxi
訓(xùn)練與驗(yàn)證損失驟升:機(jī)器學(xué)習(xí)訓(xùn)練中的異常診斷與解決方案 在機(jī)器學(xué)習(xí)模型訓(xùn)練過程中,“損失曲線” 是反映模型學(xué)習(xí)狀態(tài)的核心指 ...
2025-09-19解析 DataHub 與 Kafka:數(shù)據(jù)生態(tài)中兩類核心工具的差異與協(xié)同 在數(shù)字化轉(zhuǎn)型加速的今天,企業(yè)對(duì)數(shù)據(jù)的需求已從 “存儲(chǔ)” 轉(zhuǎn)向 “ ...
2025-09-19CDA 數(shù)據(jù)分析師:讓統(tǒng)計(jì)基本概念成為業(yè)務(wù)決策的底層邏輯 統(tǒng)計(jì)基本概念是商業(yè)數(shù)據(jù)分析的 “基礎(chǔ)語(yǔ)言”—— 從描述數(shù)據(jù)分布的 “均 ...
2025-09-19CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫(kù)表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-19SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實(shí)戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無(wú)論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢效率:打破 “拆分必慢” 的認(rèn)知誤區(qū) 在 MySQL 數(shù)據(jù)庫(kù)管理中,“大表” 始終是性能優(yōu)化繞不開的話題。 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動(dòng)態(tài)隨機(jī)一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開始提取前,需先判斷 TIF 文件的類型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價(jià)值的專業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫(kù)表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實(shí)戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫(kù))處理 Excel 數(shù)據(jù)時(shí),“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗(yàn)與 t 檢驗(yàn):差異、適用場(chǎng)景與實(shí)踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計(jì)學(xué)領(lǐng)域,假設(shè)檢驗(yàn)是驗(yàn)證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專業(yè)操盤手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計(jì)劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計(jì)劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對(duì)象的 text 與 content:區(qū)別、場(chǎng)景與實(shí)踐指南 在 Python 進(jìn)行 HTTP 網(wǎng)絡(luò)請(qǐng)求開發(fā)時(shí)(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價(jià)值的核心操盤手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫(kù)表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請(qǐng)求工具對(duì)比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請(qǐng)求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)的科學(xué)計(jì)數(shù)法問題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)時(shí)的科學(xué)計(jì)數(shù)法問題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價(jià)值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運(yùn)營(yíng)問題、提升執(zhí)行效率的核心手段,其價(jià)值 ...
2025-09-12用 SQL 驗(yàn)證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實(shí)戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計(jì)” 與 “用戶體驗(yàn) ...
2025-09-11塔吉特百貨孕婦營(yíng)銷案例:數(shù)據(jù)驅(qū)動(dòng)下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見頂” 的當(dāng)下,精準(zhǔn)營(yíng)銷成為企業(yè)突圍的核心方 ...
2025-09-11