天天看點

left join on 多條件_詳細彙總 SQL 語句 Join 連接配接方式與用法。

↑ 點選上面 “時代Java”關注我們, 關注新技術,學習新知識!

連接配接 (Join)

我們知道,連接配接可以了解為

篩選+笛卡爾乘積

,即便真實的算法可以做很多優化,基本思想不會變。笛卡爾乘積是固定的,讓連接配接操作變得複雜的,隻有

篩選

操作,也就是

篩選條件

。根據不同的篩選條件,SQL提供了不同的書寫方式,幫助程式員明确目的、減少意料之外的事情,也增加了學習的難度。隻要牢記,各種連接配接就是篩選條件不同,也就可以很容易地區分各種連接配接了。

以下挨個介紹常用的連接配接方式,同時呈現對應SQL的寫法。

自然連接配接 (Natural Join)

兩個表中含有相同的「屬性」,或稱「列」,在笛卡爾乘積的結果集中,篩選這些相同屬性相等的情況。

前文講解笛卡爾集合時的例子就是一種自然連接配接,相同的列是表達教師姓名的兩個屬性。但是, 從SQL的角度說,這個例子并不自動構成「自然連接配接」,因為兩個列的「名稱」不同。把

instructor

表中的列名改為

teacher

,或把

course

表中的列名改為

name

,才可以自動構成SQL意義下的「自連接配接」。

雖然SQL有所規定,但是我們知道,自然連接配接實際劃出了一類連接配接,代表「使用相等條件篩選」。隻要我們明确,「使用相等條件篩選」是有直接SQL支援的,就可以了。

說了這麼多,還沒說SQL怎麼寫。

最簡單的是直接使用SQL提供的關鍵字

natural join

。假設

student

表和

takes

表有相同的列

ID

,則以下兩行SQL等價:

select name, title from student natural join takes;select name, title from student, takes where student.ID = takes.ID;           

關鍵字

natural join

相當于自動添加了

where

語句,自動檢查了兩個表的哪幾個屬性相同。

也可以同時操作多個表,多寫幾個

natural join

就可以了。當然,前提是多個表都存在同樣的列。

select name, titlefrom student natural join takes natural join course;           

無論有多少個表被同時操作,SQL解釋器都會找到這些表共有的相同列,以這些列的相等為條件,篩選産生結果集。

指定連接配接條件

這樣自動找相同列的操作十分友善,同時也十分危險。要是程式員粗心地看漏了一些列名,或是對表結構有不正确的預期,可能導緻資料庫采用的相等條件比程式員設想的要多。SQL也提供了手動指定連接配接屬性的方法。

如要連接配接表示選課情況的表

takes

和課程清單

course

,指定以屬性

course_id

相等為篩選條件。以下SQL語句等價:

select name, title from takes join course using(course_id);select name, title from takes, course where takes.course_id = course.course_id;           
也可以指定多個屬性,相當于多個篩選條件:           
select name, title from takes join course using(course_id, course_name);           

join...using...

形式的語句可以很友善地指定「相等」條件,SQL同樣提供了指定其他條件的方式,也就是

join...on...

on

語句同樣可以指定相等條件,如上面的SQL等價于以下SQL:

select name, title from takes join course on takes.course_id = course.course_id;           

on

關鍵字可以指定任意篩選條件,這就可以實作任意形式的連接配接,也增加了我們學習的難度。

你可能認為,

where

關鍵字可以直接篩選笛卡爾乘積的結果,沒有必要使用

on

using

。這是個人習慣問題,見仁見智。然而,

where

通常用作對結果集最後的篩選,而

on using

專門用作連接配接時候的篩選,遵守一些規定,不但可以幫助SQL解釋器減輕負擔,還可以讓自己的工作更加清晰明了。

外連接配接 (Outer Join)

使用「自然連接配接」時,若有一個取值沒有同時出現在兩個表中,連接配接的結果集中也不會出現這個取值。若有個學生沒有選任何課程,在學生表

student

中有他,但是在選課表

takes

中沒有他,那麼以「學生姓名相等」作為連接配接篩選條件的結果集中也不會出現這個學生的名字。

在大多數情況下,我們想要這樣的效果。但是,我們有時也想保留其中一個表的資訊,不希望連接配接之後得到的表資訊缺失。這就是「外連接配接」。

當指定連接配接方式為「外連接配接」時,即便有的元素沒有同時出現在兩張表裡,也會被保留。如一個沒有選任何課的學生,指定外連接配接後,這個學生在學生表

student

中的資訊會完整出現在結果集中,而相應行中來自選課表

takes

的資訊則不會出現,以「空」顯示。可能形式如下:

left join on 多條件_詳細彙總 SQL 語句 Join 連接配接方式與用法。

外連接配接

SQL語句中對應的關鍵字是

outer join

,可以加上字首

left

right

full

,表示「左連接配接」、「右連接配接」和「全連接配接」。所謂「左右」的意思是,保留

outer join

操作符左邊或是右邊的列的資訊。如上圖的結果集,就是一個「左連接配接」的結果集,保留了左邊

student

表的資訊,而沒有保留右邊

takes

表的資訊,出現在

takes

表中的資訊要是沒有出現在

student

表中,也就不會出現在結果集中。

full

代表要同時保留兩個表的資訊,如果一個表中的資訊在另一個表中沒出現,就把沒出現的資訊顯示為「空」,道理是相同的。

上圖對應SQL語句如下,同樣使用

on

來指定連接配接條件。

select * from student left outer join takes on student.name = takes.name;           

相同的結果可以由「順序相反」的右連接配接得到:

select * from takes right outer join student on student.name = takes.name;           

上面兩個SQL得到的結果集相同。這提醒我們,無論是左連接配接還是右連接配接,甚至是全連接配接,重要的是指定好「要保留哪個表的資訊」,而不是「連接配接的名字是什麼」。

從内連接配接到外連接配接

「内連接配接」是和「外連接配接」相對的概念,也就是丢棄沒有同時出現在兩個表中的資料,也就是我們一開始介紹的連接配接方式。可以通過内連接配接了解外連接配接。

兩個表進行内連接配接後,有的行滿足内連接配接的篩選條件,有的不滿足。給不滿足篩選條件的,相應填補空值,得到的就是「外連接配接」的結果集。

由此,我們可以這樣了解:外連接配接=内連接配接+Null填補。

外連接配接條件使用

where

外連接配接的篩選條件不能用

where

寫。

考慮這樣一個SQL語句:

select * from student left outer join takes on true where student.ID = takes.ID;           

這個SQL語句,連接配接的結果集是完整的、沒有篩選過的笛卡爾乘積。把連接配接的結果集通過

where

篩選,最終結果集中一定不存在

ID

不相等的行,這不是我們想要的「外連接配接」結果。

你可以停下來想一想,有沒有可能通過

where

實作外連接配接。當然是不可能的!因為「篩選」不是外連接配接的最終結果,還有填補空值這一步,并且填補空值依賴篩選的結果。

交叉連接配接

交叉連接配接就是直接保留笛卡爾乘積的結果集,不做任何處理,沒有太多好說的。普通的逗号可以觸發交叉連接配接,如

select * from student, takes;

。也可以使用

cross join

關鍵字,如

select * from student cross join takes;

應該不需要再多解釋了。

自連接配接

同一個表可以當做兩個表來使用,出現在

join

操作符的兩邊,相同的連接配接操作仍然适用。如列出所有的工資大小關系,顯示所有「同僚A比同僚B工資高」的情況。

select higher.name as better_employee, lower.name as lower_employeefrom salary as higher join salary as loweron higher.sal > lower.sal;           

根據顯示出來的「工資比較」,你就可以去打死比你工資高的同僚了。

光是可以用來打死同僚還不太夠,「自連接配接」的操作場景更多是一種樹形結構的序列化方式,常用來處理「上下級關系」。具有上下級關系的表,通常有一個唯一表示自己的id,還有一個表示父對象的id。Linux的程序管理器就是一個很好的例子。下表是在MacOSX終端中輸入

ps -ej

得到的部分結果:

left join on 多條件_詳細彙總 SQL 語句 Join 連接配接方式與用法。

Mac程序表

PID

是「程序描述符」,

PPID

是父程序的「程序描述符」。可以看到,大多數程序的父程序都是1号程序,也就是

/sbin/launchd

,也就是Mac系統的祖宗程序。這個程序是系統啟動之後啟動的第一個程序,它的父程序的「程序描述符」是特殊值0。Mac的程序啟動次序大概如下圖,是個樹形結構。

left join on 多條件_詳細彙總 SQL 語句 Join 連接配接方式與用法。

mac boot

像Mac的程序表這樣有着樹形結構的表就是有「樹形結構」的,有唯一确定自身的id,也有「父對象」的id。當我們看到某一行,我們可以通過

PPID

找到這一行的父對象。每個對象都有一個父對象,這樣就構成了一個樹形結構,根對象的父對象用一個特殊值來表示。

可以顯示出每個程序的父程序的指令和自己的指令,SQL語句如下:

select child.command, parent.commandfrom process as child join process as parenton child.ppid = parent.pid;           

連接配接總結

要寫帶有連接配接的查詢, 需要想清楚這兩件事情:

  1. 篩選條件是什麼?
  2. 是否保留隻在一個表中出現過的資訊?

其餘的,也就很簡單啦!

--

知識分享,時代前行!

~~ 時代Java

還有更多好文章……

請檢視曆史文章和官網,

↓有分享,有收獲~

繼續閱讀