SQL 里面最常用的命令是 SELECT 語(yǔ)句,用于檢索數(shù)據(jù)。語(yǔ)法是:
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] * | expression [ AS output_name ] [, ...] [ INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table ] [ FROM from_item [, ...] ] [ WHERE condition ] [ GROUP BY expression [, ...] ] [ HAVING condition [, ...] ] [ { UNION | INTERSECT | EXCEPT [ ALL ] } select ] [ ORDER BY expression [ ASC | DESC | USING operator ] [, ...] ] [ FOR UPDATE [ OF class_name [, ...] ] ] [ LIMIT { count | ALL } [ { OFFSET | , } start ]]
現(xiàn)在我們將通過(guò)不同的例子演示 SELECT 語(yǔ)句復(fù)雜的語(yǔ)法。用于這些例子的表在 供應(yīng)商和部件數(shù)據(jù)庫(kù) 里定義。
這里是一些使用 SELECT 語(yǔ)句的簡(jiǎn)單例子:
Example 1-4. 帶有條件的簡(jiǎn)單查詢
要從表 PART 里面把字段 PRICE 大于 10 的所有記錄找出來(lái), 我們寫出下面查詢:
SELECT * FROM PART WHERE PRICE > 10;然后得到表:
PNO | PNAME | PRICE-----+---------+-------- 3 | Bolt | 15 4 | Cam | 25
在 SELECT語(yǔ)句里使用 "*" 將檢索出表中的所有屬性。 如果我們只希望從表 PART 中檢索出屬性 PNAME 和 PRICE, 我們使用下面的語(yǔ)句:
SELECT PNAME, PRICE FROM PART WHERE PRICE > 10;這回我們的結(jié)果是:
PNAME | PRICE --------+-------- Bolt | 15 Cam | 25請(qǐng)注意 SQL 的 SELECT 語(yǔ)句對(duì)應(yīng)關(guān)系演算里面的 "projection" (映射),而不是 "selection"(選擇)(參閱 關(guān)系演算 獲取詳細(xì)信息)。
WHERE 子句里的條件也可以用關(guān)鍵字 OR,AND,和 NOT 邏輯地連接起來(lái):
SELECT PNAME, PRICE FROM PART WHERE PNAME = 'Bolt' AND (PRICE = 0 OR PRICE <= 15);這樣將生成下面的結(jié)果:
PNAME | PRICE--------+-------- Bolt | 15
目標(biāo)列表和 WHERE 子句里可以使用算術(shù)操作。例如, 如果我們想知道如果我們買兩個(gè)部件的話要多少錢, 我們可以用下面的查詢:
SELECT PNAME, PRICE * 2 AS DOUBLE FROM PART WHERE PRICE * 2 < 50;
這樣我們得到: PNAME | DOUBLE--------+--------- Screw | 20 Nut | 16 Bolt | 30
請(qǐng)注意在關(guān)鍵字 AS 后面的 DOUBLE 是第二個(gè)列的新名字。 這個(gè)技巧可以用于目標(biāo)列表里的每個(gè)元素, 給它們賦予一個(gè)在結(jié)果列中顯示的新的標(biāo)題。 這個(gè)新的標(biāo)題通常稱為別名。這個(gè)別名不能在該查詢的其他地方使用。
下面的例子顯示了 SQL 里是如何實(shí)現(xiàn)連接的。
要在共同的屬性上連接三個(gè)表 SUPPLIER,PART 和 SELLS, 我們通常使用下面的語(yǔ)句:
SELECT S.SNAME, P.PNAME FROM SUPPLIER S, PART P, SELLS SE WHERE S.SNO = SE.SNO AND P.PNO = SE.PNO;而我們得到的結(jié)果是:
SNAME | PNAME-------+------- Smith | Screw Smith | Nut Jones | Cam Adams | Screw Adams | Bolt Blake | Nut Blake | Bolt Blake | Cam
在 FROM 子句里,我們?yōu)槊總€(gè)關(guān)系使用了一個(gè)別名, 因?yàn)樵谶@些關(guān)系間有著公共的命名屬性(SNO 和 PNO)。 現(xiàn)在我們可以區(qū)分不同表的公共命名屬性, 只需要簡(jiǎn)單的用每個(gè)關(guān)系的別名加上個(gè)點(diǎn)做前綴就行了。 聯(lián)合是用與 一個(gè)內(nèi)部聯(lián)接 里顯示的同樣的方法計(jì)算的。首先算出笛卡兒積 SUPPLIER × PART × SELLS 。然后選出那些滿足 WHERE 子句里給出的條件的記錄 (也就是說(shuō),公共命名屬性的值必須相等)。 最后我們映射出除 S.SNAME 和 P.PNAME 外的所有屬性。
另外一個(gè)進(jìn)行連接的方法是使用下面這樣的 SQL JOIN 語(yǔ)法:
select sname, pname from supplier JOIN sells USING (sno) JOIN part USING (pno);giving again:
sname | pname-------+------- Smith | Screw Adams | Screw Smith | Nut Blake | Nut Adams | Bolt Blake | Bolt Jones | Cam Blake | Cam(8 rows)
一個(gè)用 JOIN 語(yǔ)法創(chuàng)建的連接表,是一個(gè)出現(xiàn)在 FROM 子句里的, 在任何 WHERE,GROUP BY 或 HAVING 子句之前的表引用列表項(xiàng). 其它表引用,包括表名字或者其它 JOIN 子句,如果用逗號(hào)分隔的話, 可以包含在 FROM 子句里. 連接生成的表邏輯上和任何其它在 FROM 子句里列出的表都一樣.
SQL JOIN 有兩種主要類型,CROSS JOIN (無(wú)條件連接) 和條件連接.條件連接還可以根據(jù)聲明的 連接條件(ON,USING,或 NATURAL)和它 應(yīng)用的方式(INNER 或 OUTER 連接)進(jìn)一步細(xì)分.
連接類型
{ T1 } CROSS JOIN { T2 }
一個(gè)交叉連接(cross join)接收兩個(gè)分別有 N 行和 M 行 的表 T1 和 T2,然后返回一個(gè)包含交叉乘積 NxM 條記錄的 連接表. 對(duì)于 T1 的每行 R1,T2 的每行 R2 都與 R1 連接生成 連接的表行 JR,JR 包含所有 R1 和 R2 的字段. CROSS JOIN 實(shí)際上就是一個(gè) INNER JOIN ON TRUE.
{ T1 } [ NATURAL ] [ INNER | { LEFT | RIGHT | FULL } [ OUTER ] ] JOIN { T2 } { ON search condition | USING ( join column list ) }
一個(gè)條件 JOIN 必須通過(guò)提供一個(gè)(并且只能有一個(gè)) NATURAL,ON,或者 USING 這樣的關(guān)鍵字來(lái)聲明它的 連接條件. ON 子句 接受一個(gè) search condition, 它與一個(gè) WHERE 子句相同.USING 子句接受一個(gè)用逗號(hào)分隔的 字段名列表,連接表中必須都有這些字段, 并且用那些字段連接這些表,生成的連接表包含每個(gè)共有字段 和兩個(gè)表的所有其它字段. NATURAL 是 USING 子句的縮寫,它列出兩個(gè)表中所有公共 的字段名字.使用 USING 和 NATURAL 的副作用是 每個(gè)連接的字段都只有一份拷貝出現(xiàn)在結(jié)果表中 (與前面定義的關(guān)系演算的 JOIN 相比較).
[ INNER ] JOIN
對(duì)于 T1 的每行 R1,連接成的表在 T2 里都有一行滿 足與 R1 一起的連接條件.
對(duì)于所有 JOIN 而言,INNER 和 OUTER 都是可選的.INNER 是缺?。?nbsp;LEFT,RIGHT,和 FULL 只用于 OUTER JOIN.
LEFT [ OUTER ] JOIN
首先,執(zhí)行一次 INNER JOIN. 然后,如果 T1 里有一行對(duì)任何 T2 的行都不滿足 連接條件,那么返回一個(gè)連接行,該行的 T2 的字段 為 null.
小技巧: 連接成的表無(wú)條件地包含 T1 里的所有行.
RIGHT [ OUTER ] JOIN
首先,執(zhí)行一次 INNER JOIN. 然后,如果 T2 里有一行對(duì)任何 T1 的行都不滿足 連接條件,那么返回一個(gè)連接行,該行的 T1 的字段 為 null.
小技巧: 連接成的表無(wú)條件地包含 T2 里的所有行.
FULL [ OUTER ] JOIN
首先,執(zhí)行一次 INNER JOIN. 然后,如果 T1 里有一行對(duì)任何 T2 的行都不滿足 連接條件,那么返回一個(gè)連接行,該行的 T1 的字段 為 null. 同樣,如果 T2 里有一行對(duì)任何 T1 的行都不滿足 連接條件,那么返回一個(gè)連接行,該行的 T2 的字段 為 null.
小技巧: 連接成的表無(wú)條件地?fù)碛衼?lái)自 T1 的每 一行和來(lái)自 T2 的每一行.
所有 類型的 JOIN 都可以鏈接在一起或者嵌套在一起, 這時(shí) T1 和 T2 都可以是連接生成的表.我們可以使用圓括弧控制 JOIN 的順序,如果我們不主動(dòng)控制,那么連接順序是從左到右.
SQL 提供以一些聚集操作符(如, AVG,COUNT,SUM,MIN,MAX),這些聚集操作符以一個(gè)表達(dá)式為參數(shù)。 只要是滿足 WHERE 子句的行,就會(huì)計(jì)算這個(gè)表達(dá)式, 然后聚集操作符對(duì)這個(gè)輸入數(shù)值的集合進(jìn)行計(jì)算. 通常,一個(gè)聚集對(duì)整個(gè) SELECT 語(yǔ)句計(jì)算的結(jié)果是 生成一個(gè)結(jié)果.但如果在一個(gè)查詢里面聲明了分組, 那么數(shù)據(jù)庫(kù)將對(duì)每個(gè)組進(jìn)行一次獨(dú)立的計(jì)算,并且 聚集結(jié)果是按照各個(gè)組出現(xiàn)的(見(jiàn)下節(jié)).
Example 1-5. 聚集
果我們想知道表 PART 里面所有部件的平均價(jià)格,我們可以使用下面查詢:
SELECT AVG(PRICE) AS AVG_PRICE FROM PART;
結(jié)果是:
AVG_PRICE----------- 14.5
如果我們想知道在表 PART 里面存儲(chǔ)了多少部件,我們可以使用語(yǔ)句:
SELECT COUNT(PNO) FROM PART;得到:
COUNT------- 41.4.1.4. 分組聚集
SQL 允許我們把一個(gè)表里面的記錄分成組。 然后上面描述的聚集操作符可以應(yīng)用于這些組上 (也就是說(shuō),聚集操作符的值不再是對(duì)所有聲明的列的值進(jìn)行操作, 而是對(duì)一個(gè)組的所有值進(jìn)行操作。這樣聚集函數(shù)是為每個(gè)組獨(dú)立地進(jìn)行計(jì)算的。)
對(duì)記錄的分組是通過(guò)關(guān)鍵字 GROUP BY 實(shí)現(xiàn)的,GROUP BY 后面跟著一個(gè)定義組的構(gòu)成的屬性列表。 如果我們使用語(yǔ)句 GROUP BY A1, ⃛, Ak 我們就把關(guān)系分成了組,這樣當(dāng)且僅當(dāng)兩條記錄在所有屬性 A1, ⃛, Ak 上達(dá)成一致,它們才是同一組的。
Example 1-6. 聚集
如果我們想知道每個(gè)供應(yīng)商銷售多少個(gè)部件,我們可以這樣寫查詢:
SELECT S.SNO, S.SNAME, COUNT(SE.PNO) FROM SUPPLIER S, SELLS SE WHERE S.SNO = SE.SNO GROUP BY S.SNO, S.SNAME;得到:
SNO | SNAME | COUNT-----+-------+------- 1 | Smith | 2 2 | Jones | 1 3 | Adams | 2 4 | Blake | 3
然后我們看一看發(fā)生了什么事情。首先生成表 SUPPLIER 和 SELLS 的連接:
S.SNO | S.SNAME | SE.PNO-------+---------+-------- 1 | Smith | 1 1 | Smith | 2 2 | Jones | 4 3 | Adams | 1 3 | Adams | 3 4 | Blake | 2 4 | Blake | 3 4 | Blake | 4
然后我們把那些屬性 S.SNO 和 S.SNAME 相同的記錄放在組中:
S.SNO | S.SNAME | SE.PNO-------+---------+-------- 1 | Smith | 1 | 2-------------------------- 2 | Jones | 4-------------------------- 3 | Adams | 1 | 3-------------------------- 4 | Blake | 2 | 3 | 4
在我們的例子里,我們有四個(gè)組并且現(xiàn)在我們可以對(duì)每個(gè)組應(yīng)用聚集操作符 COUNT,生成上面給出的查詢的最終結(jié)果。
請(qǐng)注意如果要讓一個(gè)使用 GROUP BY 和聚集操作符的查詢的結(jié)果有意義, 那么用于分組的屬性也必須出現(xiàn)在目標(biāo)列表中。 所有沒(méi)有在 GROUP BY 子句里面出現(xiàn)的屬性都只能通過(guò)使用聚集函數(shù)來(lái)選擇。 否則就不會(huì)有唯一的數(shù)值與其它字段關(guān)聯(lián).
還要注意的是在聚集上聚集是沒(méi)有意義的,比如,AVG(MAX(sno)), 因?yàn)?nbsp;SELECT 只做一個(gè)回合的分組和聚集.你可以獲得這樣的結(jié)果, 方法是使用臨時(shí)表或者在 FROM 子句中使用一個(gè)子 SELECT 做第一個(gè)層次的聚集.
HAVING 子句運(yùn)做起來(lái)非常象 WHERE 子句, 只用于對(duì)那些滿足 HAVING 子句里面給出的條件的組進(jìn)行計(jì)算。 其實(shí),WHERE 在分組和聚集之前過(guò)濾掉我們不需要的輸入行, 而 HAVING 在 GROUP 之后那些不需要的組. 因此,WHERE 無(wú)法使用一個(gè)聚集函數(shù)的結(jié)果. 而另一方面,我們也沒(méi)有理由寫一個(gè)不涉及聚集函數(shù)的 HAVING. 如果你的條件不包含聚集,那么你也可以把它寫在 WHERE 里面, 這樣就可以避免對(duì)那些你準(zhǔn)備拋棄的行進(jìn)行的聚集運(yùn)算.
Example 1-7. Having
如果我們想知道那些銷售超過(guò)一個(gè)部件的供應(yīng)商,使用下面查詢:
SELECT S.SNO, S.SNAME, COUNT(SE.PNO) FROM SUPPLIER S, SELLS SE WHERE S.SNO = SE.SNO GROUP BY S.SNO, S.SNAME HAVING COUNT(SE.PNO) > 1;and get:
SNO | SNAME | COUNT-----+-------+------- 1 | Smith | 2 3 | Adams | 2 4 | Blake | 3
在 WHERE 和 HAVING 子句里,允許在任何要產(chǎn)生數(shù)值的地方使用子查詢 (子選擇)。 這種情況下,該值必須首先來(lái)自對(duì)子查詢的計(jì)算。子查詢的使用擴(kuò)展了 SQL 的表達(dá)能力。
Example 1-8. 子查詢
如果我們想知道所有比名為 'Screw' 的部件貴的部件,我們可以用下面的查詢:
SELECT * FROM PART WHERE PRICE > (SELECT PRICE FROM PART WHERE PNAME='Screw'); &&子查詢實(shí)際確定一個(gè)記錄,作為比較條件.
結(jié)果是:
PNO | PNAME | PRICE-----+---------+-------- 3 | Bolt | 15 4 | Cam | 25
當(dāng)我們檢查上面的查詢時(shí)會(huì)發(fā)現(xiàn)出現(xiàn)了兩次 SELECT 關(guān)鍵字。 第一個(gè)在查詢的開(kāi)頭 - 我們將稱之為外層 SELECT - 而另一個(gè)在 WHERE 子句里面,成為一個(gè)嵌入的查詢 - 我們將稱之為內(nèi)層 SELECT。 對(duì)外層 SELECT 的每條記錄都必須先計(jì)算內(nèi)層 SELECT。在完成所有計(jì)算之后, 我們得知名為 'Screw' 部件的記錄的價(jià)格, 然后我們就可以檢查那些價(jià)格更貴的記錄了。 (實(shí)際上,在本例中,內(nèi)層查詢只需要執(zhí)行一次, 因?yàn)樗灰蕾囉谕鈱硬樵兏叩葼顟B(tài).)
如果我們想知道那些不銷售任何部件的供應(yīng)商 (比如說(shuō),我們想把這些供應(yīng)商從數(shù)據(jù)庫(kù)中刪除),我們用:
SELECT * FROM SUPPLIER S WHERE NOT EXISTS (SELECT * FROM SELLS SE WHERE SE.SNO = S.SNO);
在我們的例子里,結(jié)果列將是空的,因?yàn)槊總€(gè)供應(yīng)商至少銷售一個(gè)部件。 請(qǐng)注意我們?cè)?nbsp;WHERE 子句的內(nèi)層 SELECT 里使用了來(lái)自外層 SELECT 的 S.SNO。 正如前面所說(shuō)的,子查詢?yōu)槊總€(gè)外層查詢計(jì)算一次,也就是說(shuō), S.SNO 的值總是從外層 SELECT 的實(shí)際記錄中取得的。
一種有些特別的子查詢的用法是把它們放在 FROM 子句里. 這個(gè)特性很有用,因?yàn)檫@樣的子查詢可以輸出多列和多行, 而在表達(dá)式里使用的子查詢必須生成一個(gè)結(jié)果. FROM 里的子查詢還可以讓我們獲得多于一個(gè)回合的分組/聚集特性, 而不需要求助于臨時(shí)表.
Example 1-9. FROM 里面的子查詢
如果我們想知道在所有我們的供應(yīng)商中的最高平均部件價(jià)格的那家, 我們不能用 MAX(AVG(PRICE)),但我們可以這么寫:
SELECT MAX(subtable.avgprice) FROM (SELECT AVG(P.PRICE) AS avgprice FROM SUPPLIER S, PART P, SELLS SE WHERE S.SNO = SE.SNO AND P.PNO = SE.PNO GROUP BY S.SNO) subtable;這個(gè)子查詢?yōu)槊總€(gè)供應(yīng)商返回一行(因?yàn)樗?nbsp;GROUP BY) 然后我們?cè)谕鈱硬樵儗?duì)所有行進(jìn)行聚集.
這些操作符分別計(jì)算兩個(gè)子查詢產(chǎn)生的元組的聯(lián)合,相交和集合理論里的相異。
Example 1-10. Union, Intersect, Except
下面的例子是 UNION 的例子:
SELECT S.SNO, S.SNAME, S.CITY FROM SUPPLIER S WHERE S.SNAME = 'Jones'UNION SELECT S.SNO, S.SNAME, S.CITY FROM SUPPLIER S WHERE S.SNAME = 'Adams';產(chǎn)生結(jié)果:
SNO | SNAME | CITY-----+-------+-------- 2 | Jones | Paris 3 | Adams | Vienna
下面是相交( INTERSECT)的例子:
SELECT S.SNO, S.SNAME, S.CITY FROM SUPPLIER S WHERE S.SNO > 1INTERSECT SELECT S.SNO, S.SNAME, S.CITY FROM SUPPLIER S WHERE S.SNO < 3;產(chǎn)生結(jié)果:
SNO | SNAME | CITY-----+-------+-------- 2 | Jones | Paris兩個(gè)查詢都會(huì)返回的元組是那條 SNO=2 的
最后是一個(gè) EXCEPT 的例子:
SELECT S.SNO, S.SNAME, S.CITY FROM SUPPLIER S WHERE S.SNO > 1EXCEPT SELECT S.SNO, S.SNAME, S.CITY FROM SUPPLIER S WHERE S.SNO > 3;結(jié)果是:
SNO | SNAME | CITY-----+-------+-------- 2 | Jones | Paris 3 | Adams | Vienna
聯(lián)系客服