SQL查询入门(中篇)
引言
??? 在前篇文章中(SQL查詢?nèi)腴T(上篇),我對(duì)數(shù)據(jù)庫查詢的基本概念以及單表查詢做了詳細(xì)的解釋,本篇文章中,主要說明SQL中的各種連接以及使用范圍,以及更進(jìn)一步的解釋關(guān)系代數(shù)法和關(guān)系演算法對(duì)在同一條查詢的不同思路。
?
多表連接簡(jiǎn)介
???? 在關(guān)系數(shù)據(jù)庫中,一個(gè)查詢往往會(huì)涉及多個(gè)表,因?yàn)楹苌儆袛?shù)據(jù)庫只有一個(gè)表,而如果大多查詢只涉及到一個(gè)表的,那么那個(gè)表也往往低于第三范式,存在大量冗余和異常。
???? 因此,連接(Join)就是一種把多個(gè)表連接成一個(gè)表的重要手段.
???? 比如簡(jiǎn)單兩個(gè)表連接學(xué)生表(Student)和班級(jí)(Class)表,如圖:
??????
?
???? 進(jìn)行連接后如圖:
?
?????
?
笛卡爾積
????? 笛卡爾積在SQL中的實(shí)現(xiàn)方式既是交叉連接(Cross Join)。所有連接方式都會(huì)先生成臨時(shí)笛卡爾積表,笛卡爾積是關(guān)系代數(shù)里的一個(gè)概念,表示兩個(gè)表中的每一行數(shù)據(jù)任意組合,上圖中兩個(gè)表連接即為笛卡爾積(交叉連接)
????? 在實(shí)際應(yīng)用中,笛卡爾積本身大多沒有什么實(shí)際用處,只有在兩個(gè)表連接時(shí)加上限制條件,才會(huì)有實(shí)際意義,下面看內(nèi)連接
內(nèi)連接
????? 如果分步驟理解的話,內(nèi)連接可以看做先對(duì)兩個(gè)表進(jìn)行了交叉連接后,再通過加上限制條件(SQL中通過關(guān)鍵字on)剔除不符合條件的行的子集,得到的結(jié)果就是內(nèi)連接了.上面的圖中,如果我加上限制條件
????? 對(duì)于開篇中的兩個(gè)表,假使查詢語句如下:
SELECT *FROM [Class] cinner join [Student] son c.ClassID=s.StudentClassID??? 可以將上面查詢語句進(jìn)行分部理解,首先先將Class表和Student表進(jìn)行交叉連接,生成如下表:
?
????
?
???? 然后通過on后面的限制條件,只選擇那些StudentClassID和ClassID相等的列(上圖中劃了綠色的部分),最終,得到選擇后的表的子集
?????
??
?????當(dāng)然,內(nèi)連接on后面的限制條件不僅僅是等號(hào),還可以使用比較運(yùn)算符,包括了>(大于)、>=(大于或等于)、<=(小于或等于)、<(小于)、!>(不大于)、!<(不小于)和<>(不等于)。當(dāng)然,限制條件所涉及的兩個(gè)列的數(shù)據(jù)類型必須匹配.
???? 對(duì)于上面的查詢語句,如果將on后面限制條件由等于改為大于:
SELECT *FROM [Class] cinner join [Student] son c.ClassID>s.StudentClassID?
???? 則結(jié)果從第一步的笛卡爾積中篩選出那些ClassID大于StudentClassID的子集:
?
?
??? 雖然上面連接后的表并沒有什么實(shí)際意義,但這里僅僅作為DEMO使用:-)
?
?????? 關(guān)系演算
?????? 上面笛卡爾積的概念是關(guān)系代數(shù)中的概念,而我在前一篇文章中提到還有關(guān)系演算的查詢方法.上面的關(guān)系代數(shù)是分布理解的,上面的語句推導(dǎo)過程是這樣的:“對(duì)表Student和Class進(jìn)行內(nèi)連接,匹配所有ClassID和StudentClassID相等行,選擇所有的列”
????? 而關(guān)系演算法,更多關(guān)注的是我想要什么,比如說上面同樣查詢,用關(guān)系演算法思考的方式是“給我找到所有學(xué)生的信息,包括他們的班級(jí)信息,班級(jí)ID,學(xué)生ID,學(xué)生姓名”
???? 用關(guān)系演算法的SQL查詢語句如下:
SELECT *FROM [Class] c,[Student] swhere c.ClassID=s.StudentClassID
?????當(dāng)然,查詢后返回的結(jié)果是不會(huì)變的:
?????
?
外連接
???? 假設(shè)還是上面兩個(gè)表,學(xué)生和班級(jí).我在學(xué)生中添加一個(gè)名為Eric的學(xué)生,但出于某種原因忘了填寫它的班級(jí)ID:
??????
???? 當(dāng)我想執(zhí)行這樣一條查詢:給我取得所有學(xué)生的姓名和他們所屬的班級(jí):
SELECT s.StudentName,c.ClassName FROM [fordemo].[dbo].[Student] sinner join [fordemo].[dbo].[Class] con s.StudentClassID=c.ClassID
?
?? 結(jié)果如下圖:
?
?
?
?? 可以看到,這個(gè)查詢“丟失”了Eric..
?? 這時(shí)就需要用到外連接,外連接可以使連接表的一方,或者雙方不必遵守on后面的連接限制條件.這里把上面的查詢語句中的inner join改為left outer join:
SELECT s.StudentName,c.ClassName FROM [fordemo].[dbo].[Student] sleft outer join [fordemo].[dbo].[Class] con s.StudentClassID=c.ClassID?
? 結(jié)果如下:
???
??? Eric又重新出現(xiàn).
????? 右外連接
????? 右外連接和左外連接的概念是相同的,只是順序不同,對(duì)于上面查詢語句,也可以改成:
SELECT s.StudentName,c.ClassName FROM [fordemo].[dbo].[Class] cright outer join [fordemo].[dbo].[Student] son s.StudentClassID=c.ClassID?
????? 效果和上面使用了左外連接的效果是一樣的.
?
??????? 全外連接
?????? 全外連接是將左邊和右邊表每行都至少輸出一次,用關(guān)鍵字”full outer join”進(jìn)行連接,可以看作是左外連接和右外連接的結(jié)合.
?
自連接
?????? 談到自連接,讓我們首先從一個(gè)表和一個(gè)問題開始:
?
????? 上面員工表(Employee),因?yàn)榻?jīng)理也是員工的一種,所以將兩種人放入一個(gè)表中,MangerID字段表示的是當(dāng)前員工的直系經(jīng)理的員工id.
????? 現(xiàn)在,我的問題是,如何查找CareySon的經(jīng)理的姓名?
???????可以看出,雖然數(shù)據(jù)存儲(chǔ)在單張表中,但除了嵌套查詢(這個(gè)會(huì)在后續(xù)文章中講到),只有自連接可以做到.正確自連接語句如下:
SELECT m.EmployeeNameFROM [fordemo].[dbo].[Employee] einner join [fordemo].[dbo].[Employee] mon e.ManagerID=m.id and e.EmployeeName='Careyson'??
???? 在詳細(xì)解釋自連接的概念之前,請(qǐng)?jiān)倏匆粋€(gè)更能說明自連接應(yīng)用之處的例子:
??????
????? 這個(gè)表是一個(gè)出席會(huì)議記錄的表,每一行表示出席會(huì)議的記錄(這里,由于表簡(jiǎn)單,我就不用EmployeeID和MeetingID來表示了,用名稱對(duì)于理解表更容易些)
?
?????? 好了,現(xiàn)在我的問題是:找出既參加“談?wù)擁?xiàng)目進(jìn)度”會(huì)議,又參加”討論職業(yè)發(fā)展”會(huì)議的員工
??????乍一看上去很讓人迷惑是吧,也許你看到這一句腦中第一印象會(huì)是:
SELECT EmployeeNameFROM [fordemo].[dbo].[MeettingRecord] mwhere MeetingName='¨???????????¨¨' and meetingName='¨???????¨°|ì?¤?é?1'
??? (我用的代碼高亮插件不支持中文,所以上面where子句后面第一個(gè)字符串是’談?wù)擁?xiàng)目進(jìn)度’,第二個(gè)是’討論職業(yè)發(fā)展’)
???? 恩,恭喜你,答錯(cuò)了…如果這樣寫將會(huì)什么數(shù)據(jù)也得不到.正確的寫法是使用自連接!
???? 自連接的是一種特殊的連接,是對(duì)物理上相同但邏輯上不相同的表進(jìn)行連接的方式。我看到百度百科上說自連接是一種特殊的內(nèi)連接,但這是錯(cuò)誤的,因?yàn)閮蓚€(gè)相同表之間不光可以內(nèi)連接,還可以外連接,交叉連接…在進(jìn)行自連接時(shí),必須為其中至少一個(gè)表指定別名以對(duì)這兩個(gè)表進(jìn)行區(qū)分!
???? 回到上面的例子,使用自連接,則正確的寫法為:
SELECT m.EmployeeNameFROM [fordemo].[dbo].[MeettingRecord] m,[fordemo].[dbo].[MeettingRecord] m2where m.MeetingName='¨???????????¨¨' and m2.MeetingName='¨???????¨°|ì?¤?é?1'and m.EmployeeName=m2.EmployeeName
?? (關(guān)于亂碼問題,請(qǐng)參考上面)
???
多表連接
?
???? 多個(gè)表連接實(shí)際上可以看成是對(duì)N個(gè)表進(jìn)行n-1次雙表連接.這樣理解會(huì)讓問題簡(jiǎn)單很多!
??????
??? 比如上面三個(gè)表,前兩個(gè)表是我們已經(jīng)在文章開始認(rèn)識(shí)的,假設(shè)現(xiàn)在又添加了一個(gè)教師表,對(duì)這三個(gè)表進(jìn)行笛卡爾積如下:
SELECT *FROM [fordemo].[dbo].[Class]cross join [fordemo].[dbo].[Teacher]cross join [fordemo].[dbo].[Student]?? 結(jié)果可以如圖表示:
???
?
總結(jié)
????? 文中對(duì)SQL中各種連接查詢方式都做了簡(jiǎn)單的介紹,并利用一些Demo實(shí)際探討各種連接的用處,掌握好各種連接的原理是寫好SQL查詢所必不可少的!
總結(jié)
以上是生活随笔為你收集整理的SQL查询入门(中篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL查询入门(上篇)
- 下一篇: SQL查询入门(下篇)