一、SQL語言簡介
SQL是結構化查詢語言(Structured Query Language)的縮寫。這種語言允許我們對數據庫進行復雜的查詢。同時也提供了創建數據庫的方法。SQL語言的使用范圍非常廣泛。許多數據庫產品都支持SQL語言,這意味著如果我們學會了SQL語言,我們可以把這種知識運用到MS Access 或SQL Server, Oracle, DB2以及非常多的其它數據庫中。
SQL語言運用在關系型數據庫中。一個關系型數據庫把數據
存儲在表(也稱關系)中。每個數據庫的主要組成就是一組表。每個表又由一組記錄組成--每條記錄在表中有相同的結構,包含固定數量的具有一定類型的字段。
下面我們來看一個實際的數據庫中的表。該表的表名為cia,包含250多條記錄,每個記錄代表一個國家。表由5個字段組成,字段的值有的是字符串類型,有的是數字類型。
name region area population gdp
---- ------ ------ ---------- -----------
Yemen Middle East 527970 14728474 23400000000
Zaire Africa 2345410 44060636 18800000000
Zambia Africa 752610 9445723 7900000000
Zimbabwe Africa 390580 11139961 17400000000
下面我們可以用一些SQL語句來查詢這個表中我們該興趣的數據。
1. 中國的GDP是多少?
查詢用的SQL語句為:
select gdp from cia
where name='china'
查詢結果為:
4800000000000
2. 給出每個地區的國家數和人口總數。并且按地區的人口數從多到少排序。
查詢用的SQL語句為:
SELECT region, COUNT(name), SUM(population)
FROM cia
GROUP BY region
ORDER BY 3 DESC
查詢結果為:
region COUNT(name) SUM(population)
------ ----------- ---------------
Asia 14 2963031109
Africa 59 793382933
Europe 43 580590872
....
怎么樣,對SQL語言有了基本的了解了吧,同時對數據庫,表,記錄,字段等一系列在SQL語言中常用的
感念也有大概的認識吧。如果不是很清楚也沒關系,在接下來的內容中我們從SQL語言中最簡單
的內容逐步給大家作介紹,并提供豐富的練習讓大家實際操作。
二、最基本的SELECT 命令
select命令或語句用來獲取一個或多個表中的記錄信息,一般配合where子句使用,來取得滿足某些條件的記錄,如果沒有where子句,將返回所有記錄。一般的使用方式如下:
SELECT attribute-list
FROM table-name
WHERE condition
attribute-list:返回內容的列表,每個內容用逗號分開。這里的內容可以為字段,包含字段的表達式或更復雜的子查詢。
table-name:表名,更復雜時可以為子查詢。
condition:條件表達式,用來篩選滿足該條件的記錄。
在本節中我們使用下表作為試驗之用:
bbc(name, region, area, population, gdp)
表名為bbc,該表有5個字段(columns),又稱為屬性(attributes) .
name :國家名
region: 國家所在的地區
area: 面積
population :人口
gdp:國民生產總值
SQL實例:
1、選出所有國家名,地區和人口
SELECT name, region, population FROM bbc
2、給出France的人口數
SELECT population FROM bbc
WHERE name = 'France'
3、哪些國家的名稱以字符D開始?
SELECT name FROM bbc
WHERE name LIKE 'D%'
4、 國土大國(面積大于五百萬平方公里)的國名和人口密度
SELECT name, population/area FROM bbc
WHERE area > 5000000
5、給出一些小(面積小于2000平方公里)而富有(國民生產總值大于50億)的國家
SELECT name , region
FROM bbc
WHERE area < 2000
AND gdp > 5000000000
三、SELECT命令中的GROUP BY 和 HAVING 子句
在介紹GROUP BY 和 HAVING 子句前,我們必需先講講sql語言中一種特殊的函數:聚合函數,例如SUM, COUNT, MAX, AVG等。這些函數和其它函數的根本區別就是它們一般作用在多條記錄上。
SELECT SUM(population) FROM bbc
這里的SUM作用在所有返回記錄的population字段上,結果就是該查詢只返回一個結果,即所有國家的總人口數。
通過使用GROUP BY 子句,可以讓SUM 和 COUNT 這些函數對屬于一組的數據起作用。
當你指定 GROUP BY region 時, 屬于同一個region(地區)的一組數據將只能返回一行值.
也就是說,表中所有除region(地區)外的字段,只能通過 SUM, COUNT等聚合函數運算后返回一個值.
HAVING子句可以讓我們篩選成組后的各組數據.
WHERE子句在聚合前先篩選記錄.也就是說作用在GROUP BY 子句和HAVING子句前.
而 HAVING子句在聚合后對組記錄進行篩選。
讓我們還是通過具體的實例來理解GROUP BY 和 HAVING 子句,還采用第三節介紹的bbc表。
SQL實例:
1、顯示每個地區的總人口數和總面積.
SELECT region, SUM(population), SUM(area)
FROM bbc
GROUP BY region
先以region把返回記錄分成多個組,這就是GROUP BY的字面含義。分完組后,然后用聚合函數對每組中的不同字段(一或多條記錄)作運算。
2、 顯示每個地區的總人口數和總面積.僅顯示那些面積超過1000000的地區。
SELECT region, SUM(population), SUM(area)
FROM bbc
GROUP BY region
HAVING SUM(area)>1000000
在這里,我們不能用where來篩選超過1000000的地區,因為表中不存在這樣一條記錄。
相反,HAVING子句可以讓我們篩選成組后的各組數據.
四、嵌套SELECT語句
嵌套SELECT語句也叫子查詢,形如:
SELECT name FROM bbc WHERE region =
(SELECT region FROM bbc WHERE name = 'Brazil')
一個 SELECT 語句的查詢結果可以作為另一個語句的輸入值。
上面的SQL語句作用為獲得和'Brazil'(巴西)同屬一個地區的所有國家。
子查詢不但可以出現在Where子句中,也可以出現在from子句中,作為一個臨時表使用,也可以出現在select list中,作為一個字段值來返回。本節我們僅介紹的Where子句中的子查詢。
在Where子句中使用子查詢,有一個在實際使用中容易犯的錯在這里說明一下。
通常,就像上面的例子一樣,嵌套的語句總是和一個值進行比較。語句 (SELECT region FROM bbc WHERE name = 'Brazil') 應該僅返回一個地區,即 'Americas'. 但如果我們在表中再插入一條地區為歐洲,國家名稱為Brazil的記錄,那會發生什么情況?
這將會導致語句的運行時錯誤.因為這個SQL語句的語法是正確的,所以數據庫引擎就開始執行,但當執行到外部的語句時就出錯了。因為這時的外部語句就像好像是 SELECT name FROM bbc WHERE region = ('Americas', 'Europe'),這個語句當然報錯了。
那么有沒有辦法解決這個問題呢,當然有。有一些SQL查詢條件允許對列表值(即多個值)進行操作。
例如"IN" 操作符,可以測試某個值是否在一個列表中。
下面的語句就可以安全的執行而不出錯,不管表中有多少條包含Brazils的記錄
SELECT name FROM bbc WHERE region IN
(SELECT region FROM bbc WHERE name = 'Brazil')
OK,
讓我們再看看一些具體的實例,
1、給出人口多于Russia(俄國)的國家名稱
SELECT name FROM bbc
WHERE population>
(SELECT population FROM bbc
WHERE name='Russia')
2、給出'India'(印度), 'Iran'(伊朗)所在地區的所有國家的所有信息
SELECT * FROM bbc
WHERE region IN
(SELECT region FROM bbc
WHERE name IN ('India','Iran'))
3、給出人均GDP超過'United Kingdom'(英國)的歐洲國家.
SELECT name FROM bbc
WHERE region='Europe' AND gdp/population >
(SELECT gdp/population FROM bbc
WHERE name='United Kingdom')
為了從兩個或多個表中選出數據,我們一般使用表連接來實現這個功能。
五、SELECT語句中的表連接(join)
從這里介紹join(連接)的概念. 為此我們準備了兩個試驗用表: album(專輯表) 和 track(曲目表).
專輯表:包含200首來自Amazon的音樂CD的概要信息。
album(asin, title, artist, price, release, label, rank)
曲目表:每張專輯中的曲目(因為是音樂CD,所以也可叫歌曲)的詳細信息。
track(album, dsk, posn, song)
SQL短語 FROM album JOIN track ON album.asin=track.album 表示連接album和track表。其中,album.asin表示專輯的惟一標識號,track.album表示曲目表中和專輯關聯的專輯號。
連接后,得到一個臨時表,該臨時表中每條記錄包含的字段由兩部分組成,除了專輯表中的對應字段album(title, artist ...),還包含曲目表的所有字段track(album, disk, posn and song)。
有了這張臨時表,很多查詢就容易實現了。
看看一些具體的實例,
1、列出歌名為'Alison'的專輯名稱和作者
SELECT title, artist
FROM album JOIN track
ON (album.asin=track.album)
WHERE song = 'Alison'
顯然,歌名、專輯名稱和作者分別在兩個表中,必需使用表連接來完成這個查詢。
2、哪個artist錄制了歌曲'Exodus'
SELECT artist
FROM album JOIN track ON (asin=album)
WHERE song = 'Exodus'
用作連接的兩個字段asin,album因為在兩個表中都是惟一的,所以不一定要加表名作為前綴。
但為了方便理解,建議使用前綴,形如:album.asin=track.album
3、列出曲目表中所有屬于'Blur'專輯的歌曲
SELECT song
FROM album JOIN track ON (asin=album)
WHERE title = 'Blur'
如果我們把 album JOIN track ON (asin=album) 看成一個臨時表的話,join的概念就很好理解了。
上節我們介紹了表連接,更確切的說是inner joins內連接.
內連接僅選出兩張表中互相匹配的記錄.因此,這會導致有時我們需要的記錄沒有包含進來。
為更好的理解這個概念,我們介紹兩個表作演示。蘇格蘭議會中的政黨表(party)和議員表(msp)。
party(Code,Name,Leader)
Code: 政黨代碼
Name: 政黨名稱
Leader: 政黨領袖
msp(Name,Party,Constituency)
Name: 議員名
Party: 議員所在政黨代碼
Constituency: 選區
六、SELECT語句中的左連接、右連接和全連接
在介紹左連接、右連接和全連接前,有一個數據庫中重要的概念要介紹一下,即空值(NULL)。
有時表中,更確切的說是某些字段值,可能會出現空值, 這是因為這個數據不知道是什么值或根本就不存在。
空值不等同于字符串中的空格,也不是數字類型的0。因此,判斷某個字段值是否為空值時不能使用=,<>這些
判斷符。必需有專用的短語:IS NULL 來選出有空值字段的記錄,同理,可用 IS NOT NULL 選出不包含空值的記錄。
例如:下面的語句選出了沒有領導者的政黨。(不要奇怪,蘇格蘭議會中確實存在這樣的政黨)
SELECT code, name FROM party
WHERE leader IS NULL
又如:一個議員被開除出黨,看看他是誰。(即該議員的政黨為空值)
SELECT name FROM msp
WHERE party IS NULL
好了,讓我們言歸正傳,看看什么叫左連接、右連接和全連接。
A left join(左連接)包含所有的左邊表中的記錄甚至是右邊表中沒有和它匹配的記錄。
同理,也存在著相同道理的 right join(右連接),即包含所有的右邊表中的記錄甚至是左邊表中沒有和它匹配的記錄。而full join(全連接)顧名思義,左右表中所有記錄都會選出來。
講到這里,有人可能要問,到底什么叫:包含所有的左邊表中的記錄甚至是右邊表中沒有和它匹配的記錄。
Ok,我們來看一個實例:
SELECT msp.name, party.name FROM msp JOIN party ON party=code
這個是我們上一節所學的Join(注意:也叫inner join),這個語句的本意是列出所有議員的名字和他所屬政黨。
你可以在 http://sqlzoo.cn/4.htm 親自執行一下該語句,看看結果是什么。
很遺憾,我們發現該查詢的結果少了兩個議員:Canavan MSP, Dennis。為什么,因為這兩個議員不屬于任和政黨,即他們的政黨字段(Party)為空值。那么為什么不屬于任何政黨就查不出來了?這是因為空值在作怪。因為議員表中政黨字段(Party)的空值在政黨表中找不到對應的記錄作匹配,即FROM msp JOIN party ON party=code 沒有把該記錄連接起來,而是過濾出去了。在該短語中,msp在Join的左邊,所有稱為左表。party在Join的右邊,所有稱為右表。
Ok,現在再看看這句話,“包含所有的左邊表中的記錄甚至是右邊表中沒有和它匹配的記錄”,意思應該很明白了吧。執行下面這個語句,那兩個沒有政黨的議員就漏不了了。
SELECT msp.name, party.name FROM msp LEFT JOIN party ON party=code
關于右連接,看看這個查詢就明白了:
SELECT msp.name, party.name FROM msp RIGHT JOIN party ON msp.party=party.code
這個查詢的結果列出所有的議員和政黨,包含沒有議員的政黨,但不包含沒有政黨的議員。
那么既要包含沒有議員的政黨,又要包含沒有政黨的議員該怎么辦呢,對了,全連接(full join)。
SELECT msp.name, party.name FROM msp FULL JOIN party ON msp.party=party.code
七、SELECT語句中的自連接
到目前為止,我們連接的都是兩張不同的表,那么能不能對一張表進行自我連接呢?答案是肯定的。有沒有必要對一張表進行自我連接呢?答案也是肯定的。
表的別名:
一張表可以自我連接。進行自連接時我們需要一個機制來區分一個表的兩個實例。
在FROM clause(子句)中我們可以給這個表取不同的別名, 然后在語句的其它需要使用到該別名的地方
用dot(點)來連接該別名和字段名。
我們在這里同樣給出兩個表來對自連接進行解釋。
愛丁堡公交線路,
車站表:
stops(id, name)
公交線路表:
route(num, company, pos, stop)
1、對公交線路表route進行自連接。
SELECT * FROM route R1, route R2
WHERE R1.num=R2.num AND R1.company=R2.company
我們route表用字段(num, company)來進行自連接. 結果是什么意思呢?你可以知道每條公交線路的任意兩個可聯通的車站。
2、用stop字段來對route(公交線路表)進行自連接。
SELECT * FROM route R1, route R2
WHERE R1.stop=R2.stop;
查詢的結果就是共用同一車站的所有公交線。這個結果對換乘是不是很有意義呢。
從這兩個例子我們可以看出,自連接的語法結構很簡單,但語意結果往往不是那么容易理解。就我們這里所列出的兩個表,如果運用得當,能解決很多實際問題,
例如,任意兩個站點之間如何換乘。
SELECT R1.company, R1.num
FROM route R1, route R2, stops S1, stops S2
WHERE R1.num=R2.num AND R1.company=R2.company
AND R1.stop=S1.id AND R2.stop=S2.id
AND S1.name='Craiglockhart'
AND S2.name='Tollcross'