本節將講述SELECT語句的一些高級功能。
列和表的別名
列的別名
精選輸出的列可以用列名、列別名或列位置在ORDER BY和GROUP BY子句引用,列位置從1開始。
例如,我們從pet表中檢索出寵物和種類,直接引用列名:
MySQL> SELECT name,species FROM pet ORDER BY name, specIEs;
其輸出為:
+----------+---------+
| name | specIEs |
+----------+---------+
| Bowser | dog |
| Buffy | dog |
| Chirpy | bird |
| Claws | cat |
| Fang | dog |
| Fluffy | cat |
| Puffball | hamster |
| Slim | snake |
| Whistler | bird |
+----------+---------+
在子句中使用列的位置:
MySQL> SELECT name,specIEs FROM pet ORDER BY 1,2;
這條語句的輸出與上面並無不同。
最後,你還可以為列命名:
MySQL> SELECT name AS n,specIEs AS s FROM pet ORDER BY n,s;
注意返回的結果:
+----------+---------+
| n | s |
+----------+---------+
| Bowser | dog |
| Buffy | dog |
| Chirpy | bird |
| Claws | cat |
| Fang | dog |
| Fluffy | cat |
| Puffball | hamster |
| Slim | snake |
| Whistler | bird |
+----------+---------+
返回的記錄順序並無不同。但是列的名字有了改變,這一點在使用CREATE TABLE…SELECT語句創建表時是有意義的。
例如,我們想從pet表生成包括其中name,owner字段的表,但是想把name和owner字段的名字重新命名為animal和child,一個很笨的方法就是創建表再錄入數據,如果使用別名,則僅僅一條SQL語句就可以解決問題,非常簡單,我們要使用的語句使CREATE TABLE:
MySQL> CREATE TABLE pet1
-> SELECT name AS animal,owner AS child
-> FROM pet;
然後,檢索生成的表,看看是否打到目的:
MySQL> SELECT * FROM pet1;
+----------+--------+
| animal | child |
+----------+--------+
| Fluffy | Harold |
| Claws | Gwen |
| Buffy | Harold |
| Chirpy | Gwen |
| Fang | Benny |
| Bowser | Diane |
| Whistler | Gwen |
| Slim | Benny |
| Puffball | Diane |
+----------+--------+
在子句中使用列的別名
你可以在GROUP BY、ORDER BY或在HAVING部分中使用別名引用列。別名也可以用來為列取一個更好點的名字:
MySQL> SELECT specIEs,COUNT(*) AS total FROM pet
-> GROUP BY specIEs HAVING total>1;
+---------+-------+
| specIEs | total |
+---------+-------+
| bird | 2 |
| cat | 2 |
| dog | 3 |
+---------+-------+
注意,你的 ANSI SQL 不允許你在一個WHERE子句中引用一個別名。這是因為在WHERE代碼被執行時,列值還可能沒有終結。例如下列查詢是不合法:
SELECT id,COUNT(*) AS total FROM pet WHERE total > 1 GROUP BY specIEs
會有下面的錯誤:
ERROR 1054: Unknown column 'total' in 'where clause'
WHERE語句被執行以確定哪些行應該包括GROUP BY部分中,而HAVING用來決定應該只用結果集合中的哪些行。
表的別名
別名不僅可以應用於列,也可以引用於表名,具體方法類似於列的別名,這裡不再重復。
列的別名經常用於表自身的連接中。你不必有2個不同的表來執行一個聯結。如果你想要將一個表的記錄與同一個表的其他記錄進行比較,聯結一個表到自身有時是有用的。例如,為了在你的寵物之中繁殖配偶,你可以用pet聯結自身來進行相似種類的雄雌配對:
MySQL> SELECT p1.name, p1.sex, p2.name, p2.sex, p1.specIEs
-> FROM pet AS p1, pet AS p2
-> WHERE p1.species = p2.specIEs AND p1.sex = "f" AND p2.sex = "m";
+--------+------+--------+------+---------+
| name | sex | name | sex | specIEs |
+--------+------+--------+------+---------+
| Fluffy | f | Claws | m | cat |
| Buffy | f | Fang | m | dog |
| Buffy | f | Bowser | m | dog |
+--------+------+--------+------+---------+
在這個查詢中,我們為表名指定別名以便能引用列並且使得每一個列引用關聯於哪個表實例更直觀。
取出互不相同的記錄
有時候你可能希望取出的數據互不重復,因為重復的數據可能對你沒有意義。
解決的辦法是使用DISTINCT關鍵字,使用這個關鍵字保證結果集中不包括重復的記錄,也就是說,你取出的記錄中,沒有重復的行。
例如,我們取出pet表中Benny所擁有的寵物的記錄:
MySQL> SELECT name,owner,specIEs,sex FROM pet WHERE owner="Benny";
+------+-------+---------+------+
| name | owner | specIEs | sex |
+------+-------+---------+------+
| Fang | Benny | dog | m |
| Slim | Benny | snake | m |
+------+-------+---------+------+
注意上面的結果,因為我們要使用它。
假定我們指定DISTINCT關鍵字,並返回列name,specIEs,sex列:
MySQL> SELECT DISTINCT name,specIEs,sex FROM pet WHERE owner="Benny";
+------+---------+------+
| name | specIEs | sex |
+------+---------+------+
| Fang | dog | m |
| Slim | snake | m |
+------+---------+------+
你可以看到有兩條結果,這是因為返回的結果集中的行不同,如果我們做以下更改,只返回owner,sex列,你可以觀察變化:
MySQL> SELECT DISTINCT owner,sex FROM pet WHERE owner="Benny";
+-------+------+
| owner | sex |
+-------+------+
| Benny | m |
+-------+------+
DISTINCT關鍵字的存在,使查詢只返回不同的記錄行。
如果一個表中,有完全相同的行,你可以使用DISTINCT,以去除冗余的輸出:
SELECT DISTINCT * FROM tbl_name
NULL值的問題
NULL值可能很奇怪直到你習慣於它。概念上,NULL意味著“沒有值”或“未知值”,且它被看作有點與眾不同的值。為了測試NULL,你不能使用算術比較運算符例如=、<或!=。為了說明它,試試下列查詢:
MySQL> SELECT 1 = NULL, 1 != NULL, 1 < NULL, 1 > NULL;
+----------+-----------+----------+----------+
| 1 = NULL | 1 != NULL | 1 < NULL | 1 > NULL |
+----------+-----------+----------+----------+
| NULL | NULL | NULL | NULL |
+----------+-----------+----------+----------+
很清楚你從這些比較中得到毫無意義的結果。相反使用IS NULL和IS NOT NULL操作符:
MySQL> SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+
| 1 IS NULL | 1 IS NOT NULL |
+-----------+---------------+
| 0 | 1 |
+-----------+---------------+
在MySQL中,0意味著假而1意味著真。
NULL這樣特殊的處理是為什麼,在前面的章節中,為了決定哪個動物不再是活著的,使用death IS NOT NULL而不是death != NULL是必要的:
MySQL> SELECT * FROM pet WHERE death IS NOT NULL;
+--------+-------+---------+------+------------+------------+
| name | owner | specIEs | sex | birth | death |
+--------+-------+---------+------+------------+------------+
| Bowser | Diane | dog | m | 1990-08-31 | 1995-07-29 |
+--------+-------+---------+------+------------+------------+
NULL值的概念是造成SQL的新手的混淆的普遍原因,他們經常認為NULL是和一個空字符串''的一樣的東西。不是這樣的!例如,下列語句是完全不同的:
MySQL> INSERT INTO my_table (phone) VALUES (NULL);
MySQL> INSERT INTO my_table (phone) VALUES ("");
兩個語句把值插入到phone列,但是第一個插入一個NULL值而第二個插入一個空字符串。第一個的含義可以認為是“電話號碼不知道”,而第二個則可意味著“她沒有電話”。
在SQL中,NULL值在於任何其他值甚至NULL值比較時總是假的(FALSE)。包含NULL的一個表達式總是產生一個NULL值,除非在包含在表達式中的運算符和函數的文檔中指出。在下列例子,所有的列返回NULL:
MySQL> SELECT NULL,1+NULL,CONCAT('Invisible',NULL);
+------+--------+--------------------------+
| NULL | 1+NULL | CONCAT('Invisible',NULL) |
+------+--------+--------------------------+
| NULL | NULL | NULL |
+------+--------+--------------------------+
如果你想要尋找值是NULL的列,你不能使用=NULL測試。下列語句不返回任何行,因為對任何表達式,expr = NULL是假的:
MySQL> SELECT * FROM my_table WHERE phone = NULL;
要想尋找NULL值,你必須使用IS NULL測試。下例顯示如何找出NULL電話號碼和空的電話號碼:
MySQL> SELECT * FROM my_table WHERE phone IS NULL;
MySQL> SELECT * FROM my_table WHERE phone = "";
在MySQL中,就像很多其他的SQL服務器一樣,你不能索引可以有NULL值的列。你必須聲明這樣的列為NOT NULL,而且,你不能插入NULL到索引的列中。
當使用ORDER BY時,首先呈現NULL值。如果你用DESC以降序排序,NULL值最後顯示。當使用GROUP BY時,所有的NULL值被認為是相等的。
為了有助於NULL的處理,你能使用IS NULL和IS NOT NULL運算符和IFNULL()函數。
對某些列類型,NULL值被特殊地處理。如果你將NULL插入表的第一個TIMESTAMP列,則插入當前的日期和時間。如果你將NULL插入一個AUTO_INCREMENT列,則插入順序中的下一個數字。
大小寫敏感性
1、數據庫和表名
在MySQL中,數據庫和表對應於在那些目錄下的目錄和文件,因而,內在的操作系統的敏感性決定數據庫和表命名的大小寫敏感性。這意味著數據庫和表名在Unix上是區分大小寫的,而在Win32上忽略大小寫。
注意:在Win32上,盡管數據庫和表名是忽略大小寫的,你不應該在同一個查詢中使用不同的大小寫來引用一個給定的數據庫和表。下列查詢將不工作,因為它作為my_table和作為MY_TABLE引用一個表:
MySQL> SELECT * FROM my_table WHERE MY_TABLE.col=1;
2、列名
列名在所有情況下都是忽略大小寫的。
3、表的別名
表的別名是區分大小寫的。下列查詢將不工作,: 因為它用a和A引用別名:
MySQL> SELECT col_name FROM tbl_name AS a
WHERE a.col_name = 1 OR A.col_name = 2;
4、列的別名
列的別名是忽略大小寫的。
5、字符串比較和模式匹配
缺省地,MySQL搜索是大小寫不敏感的(盡管有一些字符集從來不是忽略大小寫的,例如捷克語)。這意味著,如果你用col_name LIKE 'a%'搜尋,你將得到所有以A或a開始的列值。如果你想要使這個搜索大小寫敏感,使用象INDEX(col_name, "A")=0檢查一個前綴。或如果列值必須確切是"A",使用STRCMP(col_name, "A") = 0。
簡單的比較操作(>=、>、= 、< 、<=、排序和聚合)是基於每個字符的“排序值”。有同樣排序值的字符(象E,e)被視為相同的字符!LIKE比較在每個字符的大寫值上進行(“E”=”e”)。
如果你想要一個列總是被當作大小寫敏感的方式,聲明它為BINARY。
例如:
MySQL> SELECT "E"="e","E"=BINARY "e";
+---------+----------------+
| "E"="e" | "E"=BINARY "e" |
+---------+----------------+
| 1 | 0 |
+---------+----------------+
檢索語句與多個表的連接
SELECT語句不僅可以從單個表中檢索數據,也可以通過連接多個表來檢索數據。這裡將介紹全連接和左連接的作用。
我們創建兩個表作為例子。
MySQL> CREATE TABLE first
-> (
-> id TINYINT,
-> first_name CHAR(10)
-> );
錄入如下數據:
+------+-----------+
| id | first_name|
+------+-----------+
| 1 | Tom |
| 2 | Marry |
| 3 | Jarry |
+------+-----------+
MySQL> CREATE TABLE last
-> (
-> id TINYINT,
-> last_name CHAR(10)
-> );
錄入數據
+------+-----------+
| id | last_name |
+------+-----------+
| 2 | Stone |
| 3 | White |
| 4 | Donald |
+------+-----------+
全連接
全連接:在檢索時指定多個表,將每個表用都好分隔,這樣每個表的數據行都和其他表的每行交叉產生所有可能的組合,這樣就是一個全連接。所有可能的組和數即每個表的行數之和。
那麼觀察下面的檢索的結果:
MySQL> SELECT * FROM first,last;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 1 | Tom | 2 | Stone |
| 2 | Marry | 2 | Stone |
| 3 | Jarry | 2 | Stone |
| 1 | Tom | 3 | White |
| 2 | Marry | 3 | White |
| 3 | Jarry | 3 | White |
| 1 | Tom | 4 | Donald |
| 2 | Marry | 4 | Donald |
| 3 | Jarry | 4 | Donald |
+------+------------+------+-----------+
你可以看到輸出的結果集中共有3×3=9 行,這就是全連接的結果。
你也可以這樣使用SQL語句:
MySQL> SELCT first.*,last.* FROM first,last;
輸出結果與上面的例子相同,並無二致。記錄集的輸出的排序是以FROM子句後的表的順序進行,即先排列位置靠前的表,即使你改變記錄集中列的顯示順序,例如下面的例子:
MySQL> SELECT last.*,first.* FROM first,last;
+------+-----------+------+------------+
| id | last_name | id | first_name |
+------+-----------+------+------------+
| 2 | Stone | 1 | Tom |
| 2 | Stone | 2 | Marry |
| 2 | Stone | 3 | Jarry |
| 3 | White | 1 | Tom |
| 3 | White | 2 | Marry |
| 3 | White | 3 | Jarry |
| 4 | Donald | 1 | Tom |
| 4 | Donald | 2 | Marry |
| 4 | Donald | 3 | Jarry |
+------+-----------+------+------------+
上面的例子是兩個非常小的表的例子,如果是幾個非常大的表的全連接,例如,兩個行數分別為1000的表,這樣的連接可以產生非常大的結果集合1000×1000=100萬行。而實際上你並不需要這麼多行的結果,通常你需要使用一個WHERE從句來限制返回的記錄集的行數:
MySQL> SELECT * FROM first,last WHERE first.id= last.id;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 2 | Marry | 2 | Stone |
| 3 | Jarry | 3 | White |
+------+------------+------+-----------+
左連接
左連接:全連接給出FROM子句中所有表都有匹配的行。對於左連接,不僅匹配類似前面的行記錄,而且還顯示左邊的表有而右邊的表中無匹配的行。對於這樣的行,從右邊表選擇的列均被顯示為NULL。這樣,每一匹配的行都從左邊的表被選出,而如果右邊表有一個匹配的行,則被選中,如果不匹配,行仍然被選中,不過,其中右邊相應的列在結果集中均設為NULL。即,LEFT JOIN強制包含左邊表的每一行,而不管右邊表是否匹配。
語法:SELECT FROM table_reference LEFT JOIN table_reference ON conditional_expr
其中table_reference為連接的表,ON子句後接類似WHERE子句的條件。
下面我們詳細講述左連接的使用:
首先,返回一個全連接的結果集:
MySQL> SELECT * FROM first,last;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 1 | Tom | 2 | Stone |
| 2 | Marry | 2 | Stone |
| 3 | Jarry | 2 | Stone |
| 1 | Tom | 3 | White |
| 2 | Marry | 3 | White |
| 3 | Jarry | 3 | White |
| 1 | Tom | 4 | Donald |
| 2 | Marry | 4 | Donald |
| 3 | Jarry | 4 | Donald |
+------+------------+------+-----------+
注意上面的結果,下面的例子要與這個例子對照。
我們在給出一個限制條件的查詢:
MySQL> SELECT * FROM first,last WHERE first.id=last.id;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 2 | Marry | 2 | Stone |
| 3 | Jarry | 3 | White |
+------+------------+------+-----------+
這個結果類似於是從上一個全連接中選擇出first.id>last.id 的行。
現在我們給出一個真正的左連接的例子,你可以仔細觀察它的結果,要了解檢索的記錄順序:
MySQL> SELECT * FROM first LEFT JOIN last ON first.id=last.id;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 1 | Tom | NULL | NULL |
| 2 | Marry | 2 | Stone |
| 3 | Jarry | 3 | White |
+------+------------+------+-----------+
上面的結果,即用左邊表的每一行與右邊表匹配,如果匹配,則選擇到結果集中,如果沒有匹配,則結果集中,右邊表相應的列置為NULL。
為了進一步理解這一點,我們給出一個有點奇怪的例子:
MySQL> SELECT * FROM first LEFT JOIN last ON first.id=1;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 1 | Tom | 2 | Stone |
| 1 | Tom | 3 | White |
| 1 | Tom | 4 | Donald |
| 2 | Marry | NULL | NULL |
| 3 | Jarry | NULL | NULL |
+------+------------+------+-----------+
因為,在結果的最後兩行有似乎你不希望的結果。記住,如果只有ON子句的條件,那麼左邊表的每一行都會返回,只是如果沒有匹配的右邊表(雖然本例沒有約束右邊表的列),則記錄集中顯示為NULL。
前面只是幫助你理解左連接,下面LEFT JOIN的一些有用的技巧。LEFT JOIN最常見的是與WHERE子句共同使用。
使用IS NULL或者IS NOT NULL操作符可以篩選NULL或者非NULL值的列,這是最常見的技巧。
例如,選出first.id=last.id的組合,並且剔除其中沒有右表的匹配記錄:
MySQL> SELECT * FROM first LEFT JOIN last ON first.id=last.id
-> WHERE last.id IS NOT NULL;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 2 | Marry | 2 | Stone |
| 3 | Jarry | 3 | White |
+------+------------+------+-----------+
你可以看到這樣做的例子結果與語句
SELECT * FROM first,last WHERE first.id=last.id
的輸出是相同的。
又如,檢索id值只在左邊表出現,而不再右邊表出現的記錄:
MySQL> SELECT first.* FROM first LEFT JOIN last ON first.id=last.id
-> WHERE last.id IS NULL;
+------+------------+
| id | first_name |
+------+------------+
| 1 | Tom |
+------+------------+
這個語句是不能用功能相同的帶WHERE子句的全連接代替的。
注意:全連接和左連接的結果集排列順序是不同的,例如:
MySQL> SELECT * FROM first,last WHERE first.id!=last.id;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 1 | Tom | 2 | Stone |
| 3 | Jarry | 2 | Stone |
| 1 | Tom | 3 | White |
| 2 | Marry | 3 | White |
| 1 | Tom | 4 | Donald |
| 2 | Marry | 4 | Donald |
| 3 | Jarry | 4 | Donald |
+------+------------+------+-----------+
MySQL> SELECT * FROM first LEFT JOIN last ON first.id!=last.id;
+------+------------+------+-----------+
| id | first_name | id | last_name |
+------+------------+------+-----------+
| 1 | Tom | 2 | Stone |
| 1 | Tom | 3 | White |
| 1 | Tom | 4 | Donald |
| 2 | Marry | 3 | White |
| 2 | Marry | 4 | Donald |
| 3 | Jarry | 2 | Stone |
| 3 | Jarry | 4 | Donald |
+------+------------+------+-----------+
總結
本節的內容非常繁雜,各小節之間可能沒有什麼聯系,但是本節所述的都是檢索數據時很常用的技巧,主要的一些內容如下:
1、為表和列使用別名
2、注意NULL值在查詢中的使用
3、注意表名、列名、別名和字符串的大小寫問題
4、如何避免取出重復的記錄