表
存儲在表中的數(shù)據(jù)是同一種類型的數(shù)據(jù)或清單。
數(shù)據(jù)庫中的表有為一個(gè)名字來標(biāo)識自己。
表具有一些特性前酿,這些特性定義了數(shù)據(jù)在表中如何存儲,包含存儲什么樣的數(shù)據(jù)鹏溯,數(shù)據(jù)如何分解罢维,各部分信息如何命名等信息。描述表的這組信息就是所謂的模式(schema),模式可以用來描述數(shù)據(jù)庫中特定的表丙挽,也可以用來面熟整個(gè)數(shù)據(jù)庫(和其中表的關(guān)系)肺孵。
列和數(shù)據(jù)類型
表由列組成匀借。列存儲表中某部分的信息。
行
主鍵
表中的每一行都應(yīng)該有一列可以唯一標(biāo)識自己平窘。
主鍵(primary key)吓肋,一列(或一組列),其值可以唯一標(biāo)識表中每一行瑰艘。
主鍵的特性:
a.任意兩行都不具有相同的主鍵值
b.每一行都必須具有一個(gè)主鍵值
c.主鍵列的值不允許被修改和更新
d.主鍵值不能重用
什么是SQL
SQL不是某個(gè)特定數(shù)據(jù)庫供應(yīng)商專有的語言是鬼。幾乎所有重要的DBMS都支持SQL
SQL簡單易學(xué)
SQL雖然看起來很簡單,但實(shí)際上是一種強(qiáng)有力的語言紫新,靈活使用其語言元素均蜜,可以進(jìn)行非常復(fù)雜和高級的數(shù)據(jù)庫操作。
檢索數(shù)據(jù)庫
Select語句
SELECT?prod_name FROM Products;
上句表示從表Products中檢索一個(gè)名為product_name的列芒率。
Tips:
t1:結(jié)束SQL語句
多條SQL語句必須以(囤耳;)分隔
t2:SQL語句和大小寫
SQL語句不區(qū)分大小寫
t3:使用空格
在處理SQL語句是,其中所有空格都被忽略敲董。SQL語句可以寫成長長的一行紫皇,也可以分寫在多行。
檢索多個(gè)列
SELECT?prod_id,prod_name,prod_price?FROM?Products;
檢索所有列
SELECT?*?FROM?Products;
通配符(*),返回表中所有列腋寨。列的順序一般是列在表定義中出現(xiàn)的物理順序聪铺,但并不總是如此。
檢索不同的值
前述SELECT語句返回所有匹配的行萄窜,但是铃剔,如果你不希望每個(gè)值每次都出現(xiàn)。
如:SELECT?vend_id FROM Products;? 返回表products中所有產(chǎn)品供應(yīng)商的id.
SELECT DISTINCT vend_id FROM Products;
注意:
DISTINCT關(guān)鍵字作用于所有列查刻,不僅僅是跟在其后的那一列键兜。
注釋:
--?這是一條注釋(跟在命令后面)
#這是一條注釋(在一行開始處使用#,這一整行都被作為注釋穗泵。)
排序檢索數(shù)據(jù)
排序數(shù)據(jù)
SELECT?prod_name?FROM?Products;
其實(shí)普气,檢索出的數(shù)據(jù)并不是隨機(jī)顯示的。如果不排序佃延,數(shù)據(jù)一般將以它在底層表中出現(xiàn)的順序顯示现诀,這有可能是數(shù)據(jù)最初添加到表中的順序。但是履肃,如果數(shù)據(jù)隨后進(jìn)行過更新或刪除仔沿,那么這個(gè)順序?qū)艿?DBMS 重用回收存儲空間的方式的影響。因此尺棋,如果不明確控制的話封锉,則最終的結(jié)果不能(也不應(yīng)該)依賴該排序順序。關(guān)系數(shù)據(jù)庫設(shè)計(jì)理論認(rèn)為,如果不明確規(guī)定排序順序成福,則不應(yīng)該假定檢索出的數(shù)據(jù)的順序有任何意義碾局。
SELECT?prod_name?
FROM?Products
ORDER?BY?prod_name;
ORDER?BY:在指定一條ORDER BY子句時(shí),應(yīng)該保證它是SELECT語句中最后一條子句奴艾。如果它不是最后的子句擦俐,將會出現(xiàn)錯(cuò)誤消息。
按多列排序
SELECT?prod_id,prod_name,prod_price?
FROM?Products
ORDER?BY?prod_price,prod_name;
按列位置排序
SELECT?prod_id,prod_name,prod_price?
FROM?Products
ORDER?BY?2,3;
可以看到握侧,這里的輸出與上面的查詢相同,不同之處在于ORDER BY子句嘿期。SELECT清單中指定的是選擇列的相對位置而不是列名品擎。ORDER BY 2表示按SELECT清單中的第二個(gè)列prod_name進(jìn)行排序。ORDER BY 2备徐,3表示先按prod_price萄传,再按prod_name進(jìn)行排序。
這一技術(shù)的主要好處在于不用重新輸入列名蜜猾。但它也有缺點(diǎn)秀菱。首先,不明確給出列名有可能造成錯(cuò)用列名排序蹭睡。其次衍菱,在對SELECT清單進(jìn)行更改時(shí)容易錯(cuò)誤地對數(shù)據(jù)進(jìn)行排序(忘記對ORDER BY子句做相應(yīng)的改動)。最后肩豁,如果進(jìn)行排序的列不在SELECT清單中脊串,顯然不能使用這項(xiàng)技術(shù)。
指定排序方向
數(shù)據(jù)排序不限于升序排序(A-Z清钥,0-9)琼锋,這只是默認(rèn)的排序順序。還可以使用ORDER BY 子句進(jìn)行降序排序祟昭。
SELECT?prod_id,prod_name,prod_price?
FROM?Products
ORDER?BY?prod_price DESC;
SELECT?prod_id,prod_name,prod_price?
FROM?Products
ORDER?BY?prod_price?DESC,prod_name;
DESC只作用于直接位于其前面的列名缕坎。在上例中,只對prod_price列指定DESC篡悟,對prod_name列不指定谜叹。因此,prod_price列以降序排序恰力,而prod_name列(在每個(gè)價(jià)格內(nèi))仍然按標(biāo)準(zhǔn)的升序排序叉谜。
過濾數(shù)據(jù)
WHERE子句
數(shù)據(jù)庫表一般包含大量的數(shù)據(jù),很少需要檢索表中的所有行踩萎。通常只會根據(jù)特定操作或報(bào)告的需要提取表數(shù)據(jù)的子集停局。只檢索所需數(shù)據(jù)需要指定 搜索條件( search criteria ),搜索條件也稱為 過濾條件( filter condition )。
SELECT?prod_name,prod_price?
FROM?Products
WHERE?prod_price?= 3.49;
在同時(shí)使用ORDER BY和WHERE子句時(shí)董栽,應(yīng)該讓ORDER BY位于WHERE之后码倦,否則將會產(chǎn)生錯(cuò)誤(關(guān)于ORDER BY的使用,請參閱第 3 課)锭碳。
WHERE子句操作符
=等于
<>不等于
!=不等于
<小于
<=小于等于
!<不小于
>大于
>=?大于等于
!>?不大于
BETWEEN?在指定的兩個(gè)值之間? BETWEEN 5?AND 10
IS NULL? 為NULL值
SELECT?prod_name,prod_price?
FROM?Products
WHERE?prod_price? BETWEEN 5?AND 10;
SELECT?prod_name?
FROM?Products
WHERE?prod_price IS NULL;
這條語句返回所有沒有價(jià)格(空prod_price字段袁稽,不是價(jià)格為0)的產(chǎn)品,由于表中沒有這樣的行擒抛,所以沒有返回?cái)?shù)據(jù)推汽。但是,Customers表確實(shí)包含具有NULL值的列:如果沒有電子郵件地址歧沪,則cust_email列將包含NULL值歹撒。
SELECT?cust_name?
FROM?Customers
WHERE?cust_email?IS NULL;
高級數(shù)據(jù)過濾
AND? OR?
SELECT?prod_id,prod_name,prod_price?
FROM?Products
WHERE?vend_id = 'DLL01' AND prod_price <= 4;
SELECT?prod_id,prod_name,prod_price?
FROM?Products
WHERE?vend_id = 'DLL01'?OR?vend_id = 'BRS01';
求值順序
WHERE子句可以包含任意數(shù)目的AND和OR操作符。允許兩者結(jié)合以進(jìn)行復(fù)雜诊胞、高級的過濾暖夭。
SELECT?prod_id,prod_name,prod_price?
FROM?Products
WHERE (vend_id = 'DLL01' OR?vend_id = 'BRS01')?AND?prod_price <= 4;
任何時(shí)候使用具有AND和OR操作符的WHERE子句,都應(yīng)該使用圓括號明確地分組操作符撵孤。不要過分依賴默認(rèn)求值順序迈着,即使它確實(shí)如你希望的那樣。使用圓括號沒有什么壞處邪码,它能消除歧義裕菠。
IN操作符
IN操作符用來指定條件范圍,范圍中的每個(gè)條件都可以進(jìn)行匹配闭专。IN取一組由逗號分隔糕韧、括在圓括號中的合法值。
SELECT prod_name, prod_price
FROM Products
WHERE vend_id IN ( 'DLL01', 'BRS01' )
ORDER BY prod_name;
在有很多合法選項(xiàng)時(shí)喻圃,IN操作符的語法更清楚萤彩,更直觀。
在與其他AND和OR操作符組合使用IN時(shí)斧拍,求值順序更容易管理雀扶。
IN操作符一般比一組OR操作符執(zhí)行得更快(在上面這個(gè)合法選項(xiàng)很少的例子中,你看不出性能差異)肆汹。
IN的最大優(yōu)點(diǎn)是可以包含其他SELECT語句愚墓,能夠更動態(tài)地建立WHERE子句。
WHERE子句中用來指定要匹配值的清單的關(guān)鍵字昂勉,功能與OR相當(dāng)浪册。
NOT操作符
HERE子句中的NOT操作符有且只有一個(gè)功能,那就是否定其后所跟的任何條件岗照。因?yàn)镹OT從不單獨(dú)使用(它總是與其他操作符一起使用)村象,所以它的語法與其他操作符有所不同笆环。NOT關(guān)鍵字可以用在要過濾的列前,而不僅是在其后厚者。
SELECT?prod_name
FROM?Products
WHERE? NOT vend_id?= 'DLL01'
ORDER BY?prod_name;
用通配符進(jìn)行過濾
LIKE操作符
前面介紹的所有操作符都是針對已知值進(jìn)行過濾的躁劣。不管是匹配一個(gè)值還是多個(gè)值,檢驗(yàn)大于還是小于已知值库菲,或者檢查某個(gè)范圍的值账忘,其共同點(diǎn)是過濾中使用的值都是已知的。
但是熙宇,這種過濾方法并不是任何時(shí)候都好用鳖擒。例如,怎樣搜索產(chǎn)品名中包含文本 bean bag 的所有產(chǎn)品烫止?用簡單的比較操作符肯定不行败去,必須使用通配符。利用通配符烈拒,可以創(chuàng)建比較特定數(shù)據(jù)的搜索模式。在這個(gè)例子中广鳍,如果你想找出名稱包含 bean bag 的所有產(chǎn)品,可以構(gòu)造一個(gè)通配符搜索模式,找出在產(chǎn)品名的任何位置出現(xiàn) bean bag 的產(chǎn)品俱济。
通配符(wildcard):用來匹配值的一部分的特殊字符抖所。
搜索模式(search pattern):由字面值、通配符或兩者組合構(gòu)成的搜索條件祖秒。
通配符本身實(shí)際上是 SQL 的WHERE子句中有特殊含義的字符诞吱,SQL支持幾種通配符。為在搜索子句中使用通配符竭缝,必須使用LIKE操作符房维。LIKE指示 DBMS ,后跟的搜索模式利用通配符匹配而不是簡單的相等匹配進(jìn)行比較抬纸。
百分號(%)通配符
在搜索串中咙俩,%表示任何字符出現(xiàn)任意次數(shù)。例如湿故,為了找出所有以詞Fish起頭的產(chǎn)品阿趁,可發(fā)布以
下SELECT語句:
SELECT?prod_name
FROM?Products
WHERE?NOT?prod_name? LIKE 'Fish%'
SELECT prod_id,prod_name
FROM?Products
WHERE?NOT?prod_name?LIKE?'%bean bag%'
下劃線(_)通配符
另一個(gè)有用的通配符是下劃線(_)。下劃線的用途與%一樣坛猪,但它只匹配單個(gè)字符脖阵,而不是多個(gè)字符。
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear';--是兩個(gè)下劃線墅茉,所以匹配兩個(gè)字符
SELECT?prod_id, prod_name
FROM?Products
WHERE?prod_name?LIKE?'% inch teddy bear';--是兩個(gè)下劃線命黔,所以匹配兩個(gè)字符
方括號( [ ] )通配符
方括號([])通配符用來指定一個(gè)字符集呜呐,它必須匹配指定位置(通配符的位置)的一個(gè)字符。
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;
此語句的WHERE子句中的模式為'[JM]%'纷铣。這一搜索模式使用了兩個(gè)不同的通配符卵史。[JM]匹配任何以方括號中字母開頭的聯(lián)系人名,它也只能匹配單個(gè)字符搜立。因此以躯,任何多于一個(gè)字符的名字都不匹配。[JM]之后的%通配符匹配第一個(gè)字符之后的任意數(shù)目的字符啄踊,返回所需結(jié)果忧设。
此通配符可以用前綴字符^(脫字號)來否定。例如颠通,下面的查詢匹配不以J或M起頭的任意聯(lián)系人名(與前一個(gè)例子相反):
SELECT?cust_contact
FROM?Customers
WHERE?cust_contact?LIKE?'[^JM]%'
ORDER BY?cust_contact;
創(chuàng)建計(jì)算字段
需要顯示公司名址晕,同時(shí)還需要顯示公司的地址,但這兩個(gè)信息存儲在不同的表列中顿锰。
城市谨垃、州和郵政編碼存儲在不同的列中(應(yīng)該這樣),但郵件標(biāo)簽打印程序需要把它們作為一個(gè)有恰當(dāng)格式的字段檢索出來硼控。
列數(shù)據(jù)是大小寫混合的刘陶,但報(bào)表程序需要把所有數(shù)據(jù)按大寫表示出來。
物品訂單表存儲物品的價(jià)格和數(shù)量牢撼,不存儲每個(gè)物品的總價(jià)格(用價(jià)格乘以數(shù)量即可)匙隔。但為打印發(fā)票,需要物品的總價(jià)格熏版。
需要根據(jù)表數(shù)據(jù)進(jìn)行諸如總數(shù)纷责、平均數(shù)的計(jì)算。
在上述每個(gè)例子中撼短,存儲在表中的數(shù)據(jù)都不是應(yīng)用程序所需要的再膳。我們需要直接從數(shù)據(jù)庫中檢索出轉(zhuǎn)換、計(jì)算或格式化過的數(shù)據(jù)曲横,而不是檢索
出數(shù)據(jù)饵史,然后再在客戶端應(yīng)用程序中重新格式化。
這就是計(jì)算字段可以派上用場的地方了胜榔。與前幾課介紹的列不同胳喷,計(jì)算字段并不實(shí)際存在于數(shù)據(jù)庫表中。計(jì)算字段是運(yùn)行時(shí)在SELECT語句內(nèi)創(chuàng)建的夭织。
字段(field):基本上與列( column )的意思相同吭露,經(jīng)常互換使用尊惰,不過數(shù)據(jù)庫列一般稱為列讲竿,而術(shù)語字段通常與計(jì)算字段一起使用泥兰。
需要特別注意,只有數(shù)據(jù)庫知道SELECT語句中哪些列是實(shí)際的表列题禀,哪些列是計(jì)算字段鞋诗。從客戶端(如應(yīng)用程序)來看,計(jì)算字段的數(shù)據(jù)與其他列的數(shù)據(jù)的返回方式相同迈嘹。
拼接字段(concatenate)
拼接( concatenate ):將值聯(lián)結(jié)到一起(將一個(gè)值附加到另一個(gè)值)構(gòu)成單個(gè)值削彬。
解決辦法是把兩個(gè)列拼接起來。在 SQL 中的SELECT語句中秀仲,可使用一個(gè)特殊的操作符來拼接兩個(gè)列融痛。根據(jù)你所使用的 DBMS ,此操作符可用加號(+)或兩個(gè)豎杠(||)表示神僵。在 MySQL 和 MariaDB 中雁刷,必須使用特殊的函數(shù)。
SELECT vend_name + ' (' + vend_country + ')'
FROM Vendors
ORDER BY vend_name;
使用別名
從前面的輸出可以看到保礼,SELECT語句可以很好地拼接地址字段沛励。但是,這個(gè)新計(jì)算列的名字是什么呢炮障?實(shí)際上它沒有名字目派,它只是一個(gè)值。如果僅在 SQL 查詢工具中查看一下結(jié)果铝阐,這樣沒有什么不好。但是铐拐,一個(gè)未命名的列不能用于客戶端應(yīng)用中徘键,因?yàn)榭蛻舳藳]有辦法引用它。
為了解決這個(gè)問題遍蟋, SQL 支持列別名吹害。別名( alias )是一個(gè)字段或值的替換名。別名用AS關(guān)鍵字賦予虚青。
#RTRIM()函數(shù)去掉值右邊的所有空格它呀。通過使用RTRIM(),各個(gè)列都進(jìn)行了整理棒厘。
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'?
AS vend_title
FROM Vendors
ORDER BY vend_name;
SELECT語句本身與以前使用的相同纵穿,只不過這里的計(jì)算字段之后跟了文本AS vend_title。它指示 SQL 創(chuàng)建一個(gè)包含指定計(jì)算結(jié)果的名為vend_title的計(jì)算字段奢人。從輸出可以看到谓媒,結(jié)果與以前的相同,但現(xiàn)在列名為vend_title何乎,任何客戶端應(yīng)用都可以按名稱引用這個(gè)列句惯,就像它是一個(gè)實(shí)際的表列一樣土辩。
別名還有其他用途。常見的用途包括在實(shí)際的表列名包含不合法的字符(如空格)時(shí)重新命名它抢野,在原來的名字含混或容易誤解時(shí)擴(kuò)充它拷淘。
執(zhí)行算術(shù)計(jì)算
SELECT prod_id, quantity, item_price
FROM OrderItems
WHERE order_num = 20008;
SELECT? prod_id,
????????????????quantity,
?????????????????item_price,
? ? ? ? ? ? ? ? ?quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;
數(shù)據(jù)分組
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
GROUP BY子句可以包含任意數(shù)目的列,因而可以對分組進(jìn)行嵌套指孤,更細(xì)致地進(jìn)行數(shù)據(jù)分組启涯。
如果在GROUP BY子句中嵌套了分組,數(shù)據(jù)將在最后指定的分組上進(jìn)行匯總邓厕。換句話說逝嚎,在建立分組時(shí),指定的所有列都一起計(jì)算(所以不能從個(gè)別的列取回?cái)?shù)據(jù))详恼。
GROUP BY子句中列出的每一列都必須是檢索列或有效的表達(dá)式(但不能是聚集函數(shù))补君。如果在SELECT中使用表達(dá)式,則必須在GROUP BY子句中指定相同的表達(dá)式昧互。不能使用別名挽铁。
大多數(shù) SQL 實(shí)現(xiàn)不允許GROUP BY列帶有長度可變的數(shù)據(jù)類型(如文本或備注型字段)。
除聚集計(jì)算語句外敞掘,SELECT語句中的每一列都必須在GROUP BY子句中給出叽掘。
如果分組列中包含具有NULL值的行,則NULL將作為一個(gè)分組返回玖雁。如果列中有多行NULL值更扁,它們將分為一組。
GROUP BY子句必須出現(xiàn)在WHERE子句之后赫冬,ORDER BY子句之前浓镜。
過濾分組
除了能用GROUP BY分組數(shù)據(jù)外, SQL 還允許過濾分組劲厌,規(guī)定包括哪些分組膛薛,排除哪些分組。例如补鼻,你可能想要列出至少有兩個(gè)訂單的所有顧客哄啄。為此,必須基于完整的分組而不是個(gè)別的行進(jìn)行過濾风范。
我們已經(jīng)看到了WHERE子句的作用(第 4 課提及)咨跌。但是,在這個(gè)例子中WHERE不能完成任務(wù)硼婿,因?yàn)閃HERE過濾指定的是行而不是分組虑润。事實(shí)上,WHERE沒有分組的概念加酵。
那么拳喻,不使用WHERE使用什么呢哭当? SQL 為此提供了另一個(gè)子句,就是HAVING子句冗澈。HAVING非常類似于WHERE钦勘。事實(shí)上,目前為止所學(xué)過的所有類型的WHERE子句都可以用HAVING來替代亚亲。唯一的差別是彻采,WHERE過濾行,而HAVING過濾分組捌归。
SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;
HAVING 和 WHERE 的差別
這里有另一種理解方法肛响,WHERE在數(shù)據(jù)分組前進(jìn)行過濾,HAVING在數(shù)據(jù)分組后進(jìn)行過濾惜索。這是一個(gè)重要的區(qū)別特笋,WHERE排除的行不包括在分組中。這可能會改變計(jì)算值巾兆,從而影響HAVING子句中基于這些值過濾掉的分組猎物。
分組和排序
GROUP BY和ORDER BY經(jīng)常完成相同的工作,但它們非常不同角塑,理解這一點(diǎn)很重要蔫磨。表 10-1 匯總了它們之間的差別。
表 10-1 中列出的第一項(xiàng)差別極為重要圃伶。我們經(jīng)常發(fā)現(xiàn)堤如,用GROUP BY分組的數(shù)據(jù)確實(shí)是以分組順序輸出的。但并不總是這樣窒朋,這不是 SQL 規(guī)范所要求的搀罢。此外,即使特定的 DBMS 總是按給出的GROUP BY子句排序數(shù)據(jù)炼邀,用戶也可能會要求以不同的順序排序魄揉。就因?yàn)槟阋阅撤N方式分組數(shù)據(jù)(獲得特定的分組聚集值)剪侮,并不表示你需要以相同的方式排序輸出拭宁。應(yīng)該提供明確的ORDER BY子句,即使其效果等同于GROUP BY子句瓣俯。
提示:不要忘記 ORDER BY
一般在使用GROUP BY子句時(shí)杰标,應(yīng)該也給出ORDER BY子句。這是保證數(shù)據(jù)正確排序的唯一方法彩匕。千萬不要僅依賴GROUP BY排序數(shù)據(jù)腔剂。
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3;
SELECT?order_num,?COUNT(*)?AS items
FROM?OrderItems
GROUP BY?order_num
HAVING?COUNT(*) >= 3;
ORDER BY items, order_num;
聯(lián)結(jié)
創(chuàng)建聯(lián)結(jié)非常簡單,指定要聯(lián)結(jié)的所有表以及關(guān)聯(lián)它們的方式即可驼仪。
我們來看這段代碼掸犬。SELECT語句與前面所有語句一樣指定要檢索的列袜漩。這里最大的差別是所指定的兩列(prod_name和prod_price)在一個(gè)表中,而第三列(vend_name)在另一個(gè)表中湾碎。
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
現(xiàn)在來看FROM子句宙攻。與以前的SELECT語句不一樣,這條語句的FROM子句列出了兩個(gè)表:Vendors和Products介褥。它們就是這條SELECT語句聯(lián)結(jié)的兩個(gè)表的名字座掘。這兩個(gè)表用WHERE子句正確地聯(lián)結(jié),WHERE子句指示 DBMS 將Vendors表中的vend_id與Products表中的vend_id匹配起來柔滔。
可以看到溢陪,要匹配的兩列指定為Vendors.vend_id和Products.vend_id。這里需要這種完全限定列名睛廊,如果只給出vend_id形真, DBMS 就不知道指的是哪一個(gè)(每個(gè)表中有一個(gè))。從前面的輸出可以看到喉前,一條SELECT語句返回了兩個(gè)不同表中的數(shù)據(jù)没酣。
要保證所有聯(lián)結(jié)都有WHERE子句,否則 DBMS 將返回比想要的數(shù)據(jù)多得多的數(shù)據(jù)卵迂。同理裕便,要保證WHERE子句的正確性。不正確的過濾條件會導(dǎo)致 DBMS 返回不正確的數(shù)據(jù)见咒。
內(nèi)聯(lián)結(jié)
目前為止使用的聯(lián)結(jié)稱為等值聯(lián)結(jié)( equijoin )偿衰,它基于兩個(gè)表之間的相等測試。這種聯(lián)結(jié)也稱為內(nèi)聯(lián)結(jié)( inner join )改览。其實(shí)下翎,可以對這種聯(lián)結(jié)使用稍微不同的語法,明確指定聯(lián)結(jié)的類型宝当。下面的SELECT語句返回與前面例子完全相同的數(shù)據(jù):
SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
? ? ? ? ? ?ON Vendors.vend_id = Products.vend_id;
此語句中的SELECT與前面的SELECT語句相同视事,但FROM子句不同。這里庆揩,兩個(gè)表之間的關(guān)系是以INNER JOIN指定的部分FROM子句俐东。在使用這種語法時(shí),聯(lián)結(jié)條件用特定的ON子句而不是WHERE子句給出订晌。傳遞給ON的實(shí)際條件與傳遞給WHERE的相同虏辫。
創(chuàng)建高級鏈接
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
AS vend_title
FROM Vendors
ORDER BY vend_name;
SQL 除了可以對列名和計(jì)算字段使用別名,還允許給表名起別名锈拨。這樣做有兩個(gè)主要理由:
縮短 SQL 語句砌庄;
允許在一條SELECT語句中多次使用相同的表。
SELECT cust_name, cust_contact
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
? ??AND OI.order_num = O.order_num
? ??AND prod_id = 'RGAN01';
使用不同類型的聯(lián)結(jié)
迄今為止,我們使用的只是內(nèi)聯(lián)結(jié)或 等值聯(lián)結(jié)的簡單聯(lián)結(jié)÷ィ現(xiàn)在來看三種其他聯(lián)結(jié):自聯(lián)結(jié)( self-join )佩微、自然聯(lián)結(jié)( natural join )和外聯(lián)結(jié)( outer join )。
自聯(lián)結(jié)(self-join)
假如要給與 Jim Jones 同一公司的所有顧客發(fā)送一封信件萌焰。這個(gè)查詢要求首先找出 Jim Jones 工作的公司喊衫,然后找出在該公司工作的顧客。下面解決此問題的一種方法:
SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? FROM Customers
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? WHERE cust_contact = 'Jim Jones');
這是第一種解決方案杆怕,使用了子查詢族购。內(nèi)部的SELECT語句做了一個(gè)簡單檢索,返回 Jim Jones 工作公司的cust_name陵珍。該名字用于外部查詢的WHERE子句中寝杖,以檢索出為該公司工作的所有雇員。
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
? ? ? ? ? ? ? ?AND c2.cust_contact = 'Jim Jones';
此查詢中需要的兩個(gè)表實(shí)際上是相同的表互纯,因此Customers表在FROM子句中出現(xiàn)了兩次瑟幕。雖然這是完全合法的,但對Customers的引用具有歧義性留潦,因?yàn)?DBMS 不知道你引用的是哪個(gè)Customers表只盹。
解決此問題,需要使用表別名兔院。Customers第一次出現(xiàn)用了別名C1殖卑,第二次出現(xiàn)用了別名C2。現(xiàn)在可以將這些別名用作表名坊萝。例如孵稽,SELECT語句使用C1前綴明確給出所需列的全名。如果不這樣十偶, DBMS 將返回錯(cuò)誤菩鲜,因?yàn)槊麨閏ust_id、cust_name惦积、cust_contact的列各有兩個(gè)接校。 DBMS 不知道想要的是哪一列(即使它們其實(shí)是同一列)。WHERE首先聯(lián)結(jié)兩個(gè)表狮崩,然后按第二個(gè)表中的cust_contact過濾數(shù)據(jù)蛛勉,返回所需的數(shù)據(jù)。
自然聯(lián)結(jié)
無論何時(shí)對表進(jìn)行聯(lián)結(jié)厉亏,應(yīng)該至少有一列不止出現(xiàn)在一個(gè)表中(被聯(lián)結(jié)的列)董习。標(biāo)準(zhǔn)的聯(lián)結(jié)(前一課中介紹的內(nèi)聯(lián)結(jié))返回所有數(shù)據(jù)烈和,相同的列甚至多次出現(xiàn)爱只。自然聯(lián)結(jié)排除多次出現(xiàn),使每一列只返回一次招刹。
SELECT C.*, O.order_num, O.order_date,OI.prod_id, OI.quantity, OI.item_price
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
? ??????AND OI.order_num = O.order_num
? ??????AND prod_id = 'RGAN01';
外聯(lián)結(jié)
許多聯(lián)結(jié)將一個(gè)表中的行與另一個(gè)表中的行相關(guān)聯(lián)恬试,但有時(shí)候需要包含沒有關(guān)聯(lián)行的那些行窝趣。例如,可能需要使用聯(lián)結(jié)完成以下工作:
對每個(gè)顧客下的訂單進(jìn)行計(jì)數(shù)训柴,包括那些至今尚未下訂單的顧客哑舒;
列出所有產(chǎn)品以及訂購數(shù)量,包括沒有人訂購的產(chǎn)品幻馁;
計(jì)算平均銷售規(guī)模洗鸵,包括那些至今尚未下訂單的顧客。
SELECT Customers.cust_id, Orders.order_num
FROM Customers LEFT OUTER JOIN Orders
? ? ? ? ? ? ON Customers.cust_id = Orders.cust_id;
類似上一課提到的內(nèi)聯(lián)結(jié)仗嗦,這條SELECT語句使用了關(guān)鍵字OUTER JOIN來指定聯(lián)結(jié)類型(而不是在WHERE子句中指定)膘滨。但是,與內(nèi)聯(lián)結(jié)關(guān)聯(lián)兩個(gè)表中的行不同的是稀拐,外聯(lián)結(jié)還包括沒有關(guān)聯(lián)行的行火邓。在使用OUTER JOIN語法時(shí),必須使用RIGHT或LEFT關(guān)鍵字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右邊的表德撬,而LEFT指出的是OUTER JOIN左邊的表)铲咨。上面的例子使用LEFT OUTER JOIN從FROM子句左邊的表(Customers表)中選擇所有行。為了從右邊的表中選擇所有行蜓洪,需要使用RIGHT OUTER JOIN纤勒,如下例所示
SELECT?Customers.cust_id, Orders.order_num
FROM?Customers?RIGHT?OUTER JOIN?Orders
? ??????ON?Customers.cust_id = Orders.cust_id;
存在另一種外聯(lián)結(jié),就是全外聯(lián)結(jié)( full outer join )隆檀,它檢索兩個(gè)表中的所有行并關(guān)聯(lián)那些可以關(guān)聯(lián)的行踊东。與左外聯(lián)結(jié)或右外聯(lián)結(jié)包含一個(gè)表的不關(guān)聯(lián)的行不同,全外聯(lián)結(jié)包含兩個(gè)表的不關(guān)聯(lián)的行刚操。全外聯(lián)結(jié)的語法如下:
SELECT Customers.cust_id, Orders.order_num
FROM Orders FULL OUTER JOIN Customers
? ??????ON Orders.cust_id = Customers.cust_id;
警告: FULL OUTER JOIN 的支持
Access 闸翅、 MariaDB 、 MySQL 菊霜、 Open Office Base 或 SQLite 不支持FULL OUTER JOIN語法坚冀。
使用帶聚集函數(shù)的聯(lián)結(jié)
注意所使用的聯(lián)結(jié)類型。一般我們使用內(nèi)聯(lián)結(jié)鉴逞,但使用外聯(lián)結(jié)也有效记某。
關(guān)于確切的聯(lián)結(jié)語法,應(yīng)該查看具體的文檔构捡,看相應(yīng)的 DBMS 支持何種語法(大多數(shù) DBMS 使用這兩課中描述的某種語法)液南。
保證使用正確的聯(lián)結(jié)條件(不管采用哪種語法),否則會返回不正確的數(shù)據(jù)勾徽。
應(yīng)該總是提供聯(lián)結(jié)條件滑凉,否則會得出笛卡兒積。
在一個(gè)聯(lián)結(jié)中可以包含多個(gè)表,甚至可以對每個(gè)聯(lián)結(jié)采用不同的聯(lián)結(jié)類型畅姊。雖然這樣做是合法的咒钟,一般也很有用,但應(yīng)該在一起測試它們前分別測試每個(gè)聯(lián)結(jié)若未。這會使故障排除更為簡單朱嘴。
組合查詢
本課講述如何利用UNION操作符將多條SELECT語句組合成一個(gè)結(jié)果集。
組合查詢
多數(shù) SQL 查詢只包含從一個(gè)或多個(gè)表中返回?cái)?shù)據(jù)的單條SELECT語句粗合。但是萍嬉,SQL也允許執(zhí)行多個(gè)查詢(多條SELECT語句),并將結(jié)果作為一個(gè)查詢結(jié)果集返回隙疚。這些組合查詢通常稱為并(union)或復(fù)合查詢(compound query)帚湘。
主要有兩種情況需要使用組合查詢:
在一個(gè)查詢中從不同的表返回結(jié)構(gòu)數(shù)據(jù);
對一個(gè)表執(zhí)行多個(gè)查詢甚淡,按一個(gè)查詢返回?cái)?shù)據(jù)大诸。
多數(shù)情況下,組合相同表的兩個(gè)查詢所完成的工作與具有多個(gè)WHERE子句條件的一個(gè)查詢所完成的工作相同贯卦。換句話說资柔,任何具有多個(gè)WHERE子句的SELECT語句都可以作為一個(gè)組合查詢,在下面可以看到這一點(diǎn)撵割。
創(chuàng)建組合查詢
可用UNION操作符來組合數(shù)條 SQL 查詢贿堰。利用UNION,可給出多條SELECT語句啡彬,將它們的結(jié)果組合成一個(gè)結(jié)果集羹与。
使用 UNION
假如需要 Illinois 、 Indiana 和 Michigan 等美國幾個(gè)州的所有顧客的報(bào)表庶灿,還想包括不管位于哪個(gè)州的所有的Fun4All纵搁。當(dāng)然可以利用WHERE子句來完成此工作,不過這次我們使用UNION往踢。
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI');
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
組合查詢
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
為了便于參考腾誉,這里給出使用多條WHERE子句而不是UNION的相同查詢:
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
? ????????OR cust_name = 'Fun4All';
UNION規(guī)則
UNION必須由兩條或兩條以上的SELECT語句組成,語句之間用關(guān)鍵字UNION分隔(因此峻呕,如果組合四條SELECT語句利职,將要使用三個(gè)UNION關(guān)鍵字)。
UNION中的每個(gè)查詢必須包含相同的列瘦癌、表達(dá)式或聚集函數(shù)(不過猪贪,各個(gè)列不需要以相同的次序列出)。
列數(shù)據(jù)類型必須兼容:類型不必完全相同讯私,但必須是 DBMS 可以隱含轉(zhuǎn)換的類型(例如热押,不同的數(shù)值類型或不同的日期類型)西傀。
我們看看所用的SELECT語句。注意到在分別執(zhí)行語句時(shí)楞黄,第一條SELECT語句返回 3 行,第二條SELECT語句返回 2 行抡驼。而在用UNION組合兩條SELECT語句后鬼廓,只返回 4 行而不是 5 行。UNION從查詢結(jié)果集中自動去除了重復(fù)的行致盟;換句話說碎税,它的行為與一條SELECT語句中使用多個(gè)WHERE子句條件一樣。因?yàn)?Indiana 州有一個(gè)Fun4All 單位馏锡,所以兩條SELECT語句都返回該行雷蹂。使用UNION時(shí),重復(fù)的行會被自動取消杯道。
SELECT語句的輸出用ORDER BY子句排序匪煌。在用UNION組合查詢時(shí),只能使用一條ORDER BY子句党巾,它必須位于最后一條SELECT語句之后萎庭。對于結(jié)果集,不存在用一種方式排序一部分齿拂,而又用另一種方式排序另一部分的情況驳规,因此不允許使用多條ORDER BY子句。
使用函數(shù)
常用的文本處理函數(shù)
LEFT()? 返回字符串左邊的字符
LENGTH()?返回字符串的長度
LOWER()?將字符串轉(zhuǎn)換為小寫
UPPER()?將字符串轉(zhuǎn)換為大寫
LTRIM()?去掉字符串左邊的空格
RIGHT()返回字符串右邊的字符
RTRIM()去掉字符串右邊的空格
SOUNDEX()?返回字符串的SOUNDEX值
插入數(shù)據(jù)
INSERT用來將行插入(或添加)到數(shù)據(jù)庫表署海。插入有幾種方式:
插入完整的行吗购;
插入行的一部分;
插入某些查詢的結(jié)果砸狞。
INSERT INTO Customers
VALUES('1000000006',
????????????????'Toy Land',
????????????????'123 Any Street',
????????????????'New York',
????????????????'NY',
????????????????'11111',
????????????????'USA',
????????????????NULL,
????????????????NULL);
這個(gè)例子將一個(gè)新顧客插入到Customers表中捻勉。存儲到表中每一列的數(shù)據(jù)在VALUES子句中給出,必須給每一列提供一個(gè)值刀森。如果某列沒有值贯底,如上面的cust_contact和cust_email列,則應(yīng)該使用NULL值(假定表允許對該列指定空值)撒强。各列必須以它們在表定義中出現(xiàn)的次序填充禽捆。
雖然這種語法很簡單,但并不安全飘哨,應(yīng)該盡量避免使用胚想。上面的 SQL 語句高度依賴于表中列的定義次序,還依賴于其容易獲得的次序信息芽隆。即使可以得到這種次序信息浊服,也不能保證各列在下一次表結(jié)構(gòu)變動后保持完全相同的次序统屈。因此,編寫依賴于特定列次序的 SQL 語句是很不安全的牙躺,這樣做遲早會出問題愁憔。
編寫INSERT語句的更安全(不過更煩瑣)的方法如下:
INSERT INTO Customers(cust_id,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cust_name,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cust_address,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cust_city,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cust_state,
??????????????????????????????????????????cust_zip,
?????????????????????????????????????????cust_country,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?cust_contact,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cust_email)
VALUES('1000000006',
????????????????'Toy Land',
????????????????'123 Any Street',
????????????????'New York',
????????????????'NY',
????????????????'11111',
????????????????'USA',
????????????????NULL,
????????????????NULL);
這個(gè)例子與前一個(gè)INSERT語句的工作完全相同,但在表名后的括號里明確給出了列名孽拷。在插入行時(shí)吨掌, DBMS 將用VALUES列表中的相應(yīng)值填入列表中的對應(yīng)項(xiàng)。VALUES中的第一個(gè)值對應(yīng)于第一個(gè)指定列名脓恕,第二個(gè)值對應(yīng)于第二個(gè)列名膜宋,如此等等。
因?yàn)樘峁┝肆忻夺#琕ALUES必須以其指定的次序匹配指定的列名秋茫,不一定按各列出現(xiàn)在表中的實(shí)際次序。其優(yōu)點(diǎn)是乃秀,即使表的結(jié)構(gòu)改變肛著,這條INSERT語句仍然能正確工作。
插入檢索出的數(shù)據(jù)
插入部分行
使用視圖
下面是視圖的一些常見應(yīng)用跺讯。
重用 SQL 語句策泣。
簡化復(fù)雜的 SQL 操作。在編寫查詢后抬吟,可以方便地重用它而不必知道其基本查詢細(xì)節(jié)萨咕。
使用表的一部分而不是整個(gè)表。
保護(hù)數(shù)據(jù)火本∥6樱可以授予用戶訪問表的特定部分的權(quán)限,而不是整個(gè)表的訪問權(quán)限钙畔。
更改數(shù)據(jù)格式和表示茫陆。視圖可返回與底層表的表示和格式不同的數(shù)據(jù)。
創(chuàng)建視圖之后擎析,可以用與表基本相同的方式使用它們簿盅。可以對視圖執(zhí)行SELECT操作揍魂,過濾和排序數(shù)據(jù)桨醋,將視圖聯(lián)結(jié)到其他視圖或表,甚至添加和更新數(shù)據(jù)(添加和更新數(shù)據(jù)存在某些限制现斋,關(guān)于這個(gè)內(nèi)容稍后做介紹)喜最。
重要的是,要知道視圖僅僅是用來查看存儲在別處數(shù)據(jù)的一種設(shè)施庄蹋。視圖本身不包含數(shù)據(jù)瞬内,因此返回的數(shù)據(jù)是從其他表中檢索出來的迷雪。在添加或更改這些表中的數(shù)據(jù)時(shí),視圖將返回改變過的數(shù)據(jù)虫蝶。
下面是關(guān)于視圖創(chuàng)建和使用的一些最常見的規(guī)則和限制章咧。
與表一樣,視圖必須唯一命名(不能給視圖取與別的視圖或表相同的名字)能真。
對于可以創(chuàng)建的視圖數(shù)目沒有限制赁严。
創(chuàng)建視圖,必須具有足夠的訪問權(quán)限舟陆。這些權(quán)限通常由數(shù)據(jù)庫管理人員授予误澳。
視圖可以嵌套耻矮,即可以利用從其他視圖中檢索數(shù)據(jù)的查詢來構(gòu)造視圖秦躯。所允許的嵌套層數(shù)在不同的 DBMS 中有所不同(嵌套視圖可能會嚴(yán)
重降低查詢的性能,因此在產(chǎn)品環(huán)境中使用之前裆装,應(yīng)該對其進(jìn)行全面測試)踱承。
許多 DBMS 禁止在視圖查詢中使用ORDER BY子句。
有些 DBMS 要求對返回的所有列進(jìn)行命名哨免,如果列是計(jì)算字段茎活,則需要使用別名(關(guān)于列別名的更多信息,請參閱第 7 課)琢唾。
視圖不能索引载荔,也不能有關(guān)聯(lián)的觸發(fā)器或默認(rèn)值。
有些 DBMS 把視圖作為只讀的查詢采桃,這表示可以從視圖檢索數(shù)據(jù)懒熙,但不能將數(shù)據(jù)寫回底層表。詳情請參閱具體的 DBMS 文檔普办。
有些 DBMS 允許創(chuàng)建這樣的視圖工扎,它不能進(jìn)行導(dǎo)致行不再屬于視圖的插入或更新。例如有一個(gè)視圖衔蹲,只檢索帶有電子郵件地址的顧客肢娘。如
果更新某個(gè)顧客,刪除他的電子郵件地址舆驶,將使該顧客不再屬于視圖橱健。這是默認(rèn)行為,而且是允許的沙廉,但有的 DBMS 可能會防止這種情況發(fā)生畴博。
CASE語句
case when condition1 then 取值1
? ? ? ? ??when condition2 then 取值2
? ? ? ? ? else?取值3 end
row_number() over (partition by variable1 order by variable2) rank
按variable1分組并在每個(gè)組內(nèi)按variable2排序,最終生成序號蓝仲,并將其命名為RANK.