除非最終檢索它們並利用它們來做點事情,否則將記錄放入數據庫沒什麼好處。這就是SELECT 語句的用途,即幫助取出數據。SELECT 大概是SQL 語言中最常用的語句,而且怎樣使用它也最為講究;用它來選擇記錄可能相當復雜,可能會涉及許多表中列之間的比較。SELECT 語句的語法如下:
除了詞“ S E L E C T”和說明希望檢索什麼的column_list 部分外,語法中的每樣東西都是可選的。有的數據庫還需要FROM 子句。MySQL有所不同,它允許對表達式求值而不引用任何表:
在第1章中,我們對SELECT 語句下了很大的功夫,主要集中介紹了列選擇的列表和WHERE、GROUP BY、ORDER BY、HAVING 以及LIMIT 子句。本章中,我們將主要精力放在SELECT 語句中最可能令人搞不清的方面,即連接( j o i n)上。我們將介紹MySQL支持的連接類型、它們的含義、怎樣指定它們等。這樣做將有助於更有效地使用MySQL,因為在許多情況下,解決怎樣編寫查詢的關鍵是確定怎樣將表恰當地連接在一起。還應該參閱一下本章後面3 . 8節“解決方案隨筆”。在那一節中將會找到解決幾個SQL 問題的方案,它們多數
都涉及SELECT 語句這樣或那樣的功能。
使用SELECT 的一個問題是,在第一次遇到一種新的問題時,並不總是能夠知道怎樣編寫SELECT 查詢來解決它。但在解決以後,再遇到類似的問題時,可利用其中的經驗。SELECT 大概是過去的經驗在能夠有效地使用中起很大作用的語句,這是因為使用它的方法太多的原故。
在有了一定的經驗後,可將這些經驗用於新問題,您會發現自己思考問題類似於,“噢,是的,它就是一個LEFT JOIN 問題。”或者,“啊哈,這就是一個受各對索引列制約的三路線連接。”(指出這一點,實際上我也感到有點不願意。聽到經驗有幫助,您可能受到一定的鼓舞。另外,考慮到您最終能那樣思考問題也會令自己有點驚訝。)下幾節中介紹怎樣利用MySQL支持的連接操作的格式,多數例子使用了下面的兩個表。它們很小,很簡單,足以很清楚地看出每種連接的效果。
3.6.1平凡連接
最簡單的連接是平凡連接( trivial join),這種連接中只指定一個表。在此情況下,行從指定的表中選擇。如:
有的作者根本就不考慮這種SELECT 連接的形式,僅對從兩個或多個表中檢索記錄的SELECT 語句使用“連接”這個術語。本人認為那只是看法不同而已。
3.6.2 全連接
如果指定多個表,將各個表名用逗號分隔,就指定了全連接。例如,如果連接兩個表,來自第一個表中的每行與第二個表中每行進行組合:
全連接也稱為叉連接,因為每個表的每行都與其他表中的每行交叉以產生所有可能的組合。這也就是所謂的笛卡兒積。這樣連接表潛在地產生數量非常大的行,因為可能得到的行數為每個表中行數之積。三個分別含有10 0、2 0 0、3 0 0行的表的全連接將產生10 0×2 0 0×3 0 0= 6百萬行。即使各表很小,所得到的行數也會很大。在這樣的情形下,通常要使用WHERE
子句來將結果集減少為易於管理的大小。
如果在WHERE 子句中增加一個條件使各表在某些列上進行匹配,此連接就是所謂的等同連接(e q ui - j o i n),因為只選擇那些在指定列中具有相等的值的行。如:
J O I N、CROSS JOIN 和INNER JOIN 連接類型都與“,”連接操作符意義相同。STRAIGHT_JOIN 與全連接類似,但各表按FROM 子句中指定的次序進行連接。一般情況下,在全連接中MySQL優化程序自身完全不考慮安排各表的順序,以便使記錄的檢索更快。在有的場合,優化程序將作出非優化的選擇,這樣將忽略STRAIGHT_JOIN 關鍵字。在SELECT 語句中,可在兩個位置給出S T R A I G H T _ J O I N。一個位置是在SELECT 關鍵字與選擇列表之間,將其放在這裡對語句中所有全連接具有整體作用。另一個在FROM 子句中。下面兩條語句是等價的:
限定列引用
SELECT 語句中列的引用必須對FROM 子句中指定的每個表是無歧義的。如果FROM 子句中僅指定了一個表,則無歧義存在,因為所有列必須是該表的列。如果指定了多個表,只出現在一個表中的列名也是無歧義的。但是,如果某個列名出現在多個表中,該列的引用必須用表名來限定,用tbl_name.col_name 語法來表明所指的是哪個表。如果表my_tbl1含有列a 和b,表my_tbl2 含有列b 和c,則列a 和c 的引用是無歧義的,但b 的引用必須限定為my_tbl1.b 或my _ t b l 2 . b,如:
有時,表名限定符還不能解決列的引用問題。例如,如果在一個查詢中多次使用一個表,用表名限定列名沒有什麼用處。在此情況下,為表達您的想法可使用別名。給表指派一個別名,利用這個別名來引用列,其語法為: alias _ name.col _ name。下面的查詢將表與
自身進行連接,給表指派了一個別名,以便應付引用列時有歧義的情況:
3.6.3 左連接
等價連接只給出兩個表匹配的行。左連接也給出匹配行,但它還顯示左邊表中有的但在右邊表中無匹配的行。對於這樣的行,從右邊表中選擇的列都顯示為NULL。這樣,每一行都從左邊表中選出。如果右邊表中有一個匹配行,則該行被選中。如果不匹配,行仍然被選中,但它是一個“假”行,其中所有列被設置為NULL。換句話說,LEFT JOIN 強制結果集包含對應左邊表中每一行的行,而不管左邊表中的行在右邊表中是否有匹配的行。匹配是根據ON 或USING( ) 子句中給出的列進行的。不管所連接的列是否具有相同的名稱,都可使用ON。如:
USING( ) 子句類似於O N,但連接列的名稱必須在每個表中是相同的。下面的查詢將my_tbl1.b 連接到my _ t b l 2 . b:
在希望只查找出現在左邊表而不出現在右邊表中的行時, LEFT JOIN 極為有用。可通過增加一條查詢右邊表中具有NULL 值的列的WHERE 子句來完成這項工作。
一般不用擔心選擇為NULL 的列,因為沒有什麼意思。真正要關心的是左邊表中不匹配的列,如:
利用LEFT JOIN 時有一件事情需要提防,如果所連接的列未定義為NOT NULL,將會在結果中得出一些無關的行。
LEFT JOIN 有幾個同義詞和變種。LEFT OUTER JOIN 為LEFT JOIN 的一個同義詞。
LEFT JOIN 還有一個為MySQL所接受的ODBC 表示如下(“o j”意為“outer join”):
N ATURAL LEFT JOIN 類似於LEFT JOIN;它執行一個LEFT JOIN,匹配左邊表和右邊表中具有相同名稱的所有列。
有的數據庫還有,RIGHT JOIN,但MySQL迄今還沒有。