快速過(guò)完這本書(shū)..... 只記了些容易忘或者不太熟悉的點(diǎn)
第二章 MySQL簡(jiǎn)介
MySQL版本主要修改
4 InnoDB引擎,增加事務(wù)處理(第26章)、并(第17章)、改 進(jìn)全文本搜索(第18章)等的支持。
4.1 對(duì)函數(shù)庫(kù)、子查詢(第14章)、集成幫助等的重要增加。
5 存儲(chǔ)過(guò)程(第23章)、觸發(fā)器(第25章)、游標(biāo)(第24章)、
視圖(第22章)等
第四章 檢索數(shù)據(jù)
SQL語(yǔ)句和大小寫(xiě) 請(qǐng)注意,SQL語(yǔ)句不區(qū)分大小寫(xiě),因此 SELECT與select是相同的。同樣,寫(xiě)成Select也沒(méi)有關(guān)系。 許多SQL開(kāi)發(fā)人員喜歡對(duì)所有SQL關(guān)鍵字使用大寫(xiě),而對(duì)所有 列和表名使用小寫(xiě),這樣做使代碼更易于閱讀和調(diào)試。
檢索不同的行
SELECT DISTINCT vend_id
//告訴MySQL只返回不同(唯一)的 vend_id行 輸出
*----------*
| vend_id |
| 1001 |
| 1002 |
| 1003 |
*----------*
不能部分使用DISTINCT DISTINCT關(guān)鍵字應(yīng)用于所有列而 不僅是前置它的列。如果給出SELECT DISTINCT vend_id, prod_price,除非指定的兩個(gè)列都不同,否則所有行都將被 檢索出來(lái)。
第五章 排序
5.2 按多個(gè)列排序
select * from xxx order by prod_price, prod_name
僅在多個(gè)行具有相同的prod_price值時(shí)才對(duì)產(chǎn)品按prod_name進(jìn)行排序。
區(qū)分大小寫(xiě)和排序順序
在對(duì)文本性的數(shù)據(jù)進(jìn)行排序時(shí),A與a相同嗎?a位于B之前還是位于Z之后?這些問(wèn)題不是理論問(wèn) 題,其答案取決于數(shù)據(jù)庫(kù)如何設(shè)置。
在字典(dictionary)排序順序中,A被視為與a相同,這是MySQL (和大多數(shù)數(shù)據(jù)庫(kù)管理系統(tǒng))的默認(rèn)行為。但是,許多數(shù)據(jù)庫(kù) 管理員能夠在需要時(shí)改變這種行為(如果你的數(shù)據(jù)庫(kù)包含大量
外語(yǔ)字符,可能必須這樣做)。
這里,關(guān)鍵的問(wèn)題是,如果確實(shí)需要改變這種排序順序,用簡(jiǎn) 單的ORDER BY子句做不到。你必須請(qǐng)求數(shù)據(jù)庫(kù)管理員的幫助。
第六章 過(guò)濾數(shù)據(jù)
6.2.4 空值檢查
在創(chuàng)建表時(shí),表設(shè)計(jì)人員可以指定其中的列是否可以不包含值。在
一個(gè)列不包含值時(shí),稱其為包含空值NULL。
SELECT語(yǔ)句有一個(gè)特殊的WHERE子句,可用來(lái)檢查具有NULL值的列。
這個(gè)WHERE子句就是IS NULL子句。
select * from xxx where prod_price IS NULL
第八章 用通配符過(guò)濾
8.1.1 百分號(hào)(%)通配符
最常使用的通配符是百分號(hào)(%)。在搜索串中,%表示任何字符出現(xiàn) 任意次數(shù)。例如,為了找出所有以詞jet起頭的產(chǎn)品,可使用以下SELECT 語(yǔ)句:
SELECT * FROM products WHERE prod_name LIKE 'jet%'
此例子使用了搜索模式'jet%'。在執(zhí)行這條子句時(shí),將檢索任
意以jet起頭的詞。%告訴MySQL接受jet之后的任意字符,不 管它有多少字符。
區(qū)分大小寫(xiě) 根據(jù)MySQL的配置方式,搜索可以是區(qū)分大小 寫(xiě)的。如果區(qū)分大小寫(xiě),'jet%'與JetPack 1000將不匹配。
通配符可在搜索模式中任意位置使用,并且可以使用多個(gè)通配符。 下面的例子使用兩個(gè)通配符,它們位于模式的兩端:
SELECT * FROM products WHERE prod_name LIKE '%jet%'
注意尾空格
尾空格可能會(huì)干擾通配符匹配。例如,在保存詞 anvil時(shí),如果它后面有一個(gè)或多個(gè)空格,則子句WHERE prod_name LIKE '%anvil'將不會(huì)匹配它們,因?yàn)樵谧詈蟮膌 后有多余的字符。解決這個(gè)問(wèn)題的一個(gè)簡(jiǎn)單的辦法是在搜索模 式最后附加一個(gè)%。一個(gè)更好的辦法是使用函數(shù)(第11章將會(huì) 介紹)去掉首尾空格。
注意NULL
雖然似乎%通配符可以匹配任何東西,但有一個(gè)例 外,即NULL。即使是WHERE prod_name LIKE '%'也不能匹配 用值NULL作為產(chǎn)品名的行。
8.1.2 下劃線(_)通配符
另一個(gè)有用的通配符是下劃線()。下劃線的用途與%一樣,但下劃
線只匹配單個(gè)字符而不是多個(gè)字符。
與%能匹配0個(gè)字符不一樣,總是匹配一個(gè)字符,不能多也不能少。
8.2 使用通配符的技巧
正如所見(jiàn),MySQL的通配符很有用。但這種功能是有代價(jià)的:通配 符搜索的處理一般要比前面討論的其他搜索所花時(shí)間更長(zhǎng)。這里給出一 些使用通配符要記住的技巧。
- 不要過(guò)度使用通配符。如果其他操作符能達(dá)到相同的目的,應(yīng)該 使用其他操作符。
- 在確實(shí)需要使用通配符時(shí),除非絕對(duì)有必要,否則不要把它們用 在搜索模式的開(kāi)始處。把通配符置于搜索模式的開(kāi)始處,搜索起 來(lái)是最慢的。
- 仔細(xì)注意通配符的位置。如果放錯(cuò)地方,可能不會(huì)返回想要的數(shù)據(jù)
第九章 用正則表達(dá)式 進(jìn)行搜索
僅為正則表達(dá)式語(yǔ)言的一個(gè)子集 如果你熟悉正則表達(dá)式,需 要注意:MySQL僅支持多數(shù)正則表達(dá)式實(shí)現(xiàn)的一個(gè)很小的子 集。本章介紹MySQL支持的大多數(shù)內(nèi)容。
9.2.1 基本字符匹配
SELECT * FROM product WHERE product_name REGEXP '1000'
除關(guān)鍵字LIKE被REGEXP替代外,這條語(yǔ)句看上去非常像使用
LIKE的語(yǔ)句(第8章)。它告訴MySQL:REGEXP后所跟的東西作 為正則表達(dá)式(與文字正文1000匹配的一個(gè)正則表達(dá)式)處理。
*----------*
| product_name |
| JetPack 1000 |
*----------*
支持操作符
.
|
[]
[1-5]
\\ (轉(zhuǎn)義,比如查找. 需要\\.)
9.2.6 匹配字符類
類 | 說(shuō)明 |
---|---|
[:alnum:] | 任意字母和數(shù)字(同[a-zA-Z0-9]) |
[:alpha:] | 任意字符(同[a-zA-Z]) |
[:blank:] | 空格和制表(同[\t]) |
[:cntrl:] | ASCII控制字符(ASCII 0到31和127) |
[:digit:] | 任意數(shù)字(同[0-9]) |
[:graph:] | 與[:print:]相同,但不包括空格 |
[:lower:] | 任意小寫(xiě)字母(同[a-z]) |
[:print:] | 任意可打印字符 |
[:punct:] | 既不在[:alnum:]又不在[:cntrl:]中的任意字符 |
[:space:] | 包括空格在內(nèi)的任意空白字符(同[\f\n\r\t\v]) |
[:upper:] | 任意大寫(xiě)字母(同[A-Z]) |
[:xdigit:] | 任意十六進(jìn)制數(shù)字(同[a-fA-F0-9]) |
第十章、創(chuàng)建計(jì)算字段
10.2 拼接字段
vendors表包含供應(yīng)商名和位置信息。假如要生成一個(gè)供應(yīng)商報(bào)表, 需要在供應(yīng)商的名字中按照name(location)這樣的格式列出供應(yīng)商的位置。
此報(bào)表需要單個(gè)值,而表中數(shù)據(jù)存儲(chǔ)在兩個(gè)列vend_name和vend_ country中。此外,需要用括號(hào)將vend_country括起來(lái),這些東西都沒(méi)有 明確存儲(chǔ)在數(shù)據(jù)庫(kù)表中。我們來(lái)看看怎樣編寫(xiě)返回供應(yīng)商名和位置的 SELECT語(yǔ)句。
SELECT Concat(vend_name, ' (', vend_country, ') ') FROM vendor;
此外還可以疊加去空格
SELECT Concat(RTrim(vend_name), ' (', vend_country, ') ') FROM vendor;
RTrim()函數(shù)去掉值右邊的所有空格。通過(guò)使用RTrim(),各個(gè) 列都進(jìn)行了整理。
Trim函數(shù) MySQL除了支持RTrim()(正如剛才所見(jiàn),它去掉 串右邊的空格),還支持LTrim()(去掉串左邊的空格)以及 Trim()(去掉串左右兩邊的空格)。
使用別名
別名用AS關(guān)鍵字賦予
SELECT Concat(RTrim(vend_name), ' (', vend_country, ') ') as vend_title FROM vendor;
10.3 使用算數(shù)運(yùn)算
支持加減乘除 + — * /
第十一章 使用數(shù)據(jù)處理函數(shù)
函數(shù)沒(méi)有SQL的可移植性強(qiáng)
能運(yùn)行在多個(gè)系統(tǒng)上的代碼稱 為可移植的(portable)。相對(duì)來(lái)說(shuō),多數(shù)SQL語(yǔ)句是可移植的, 在SQL實(shí)現(xiàn)之間有差異時(shí),這些差異通常不那么難處理。而函 數(shù)的可移植性卻不強(qiáng)。幾乎每種主要的DBMS的實(shí)現(xiàn)都支持其 他實(shí)現(xiàn)不支持的函數(shù),而且有時(shí)差異還很大。
為了代碼的可移植,許多SQL程序員不贊成使用特殊實(shí)現(xiàn)的功 能。雖然這樣做很有好處,但不總是利于應(yīng)用程序的性能。如 果不使用這些函數(shù),編寫(xiě)某些應(yīng)用程序代碼會(huì)很艱難。必須利 用其他方法來(lái)實(shí)現(xiàn)DBMS非常有效地完成的工作。
如果你決定使用函數(shù),應(yīng)該保證做好代碼注釋,以便以后你(或 其他人)能確切地知道所編寫(xiě)SQL代碼的含義。
常用文本處理函數(shù)
Left() 返回串左邊的字符
Length() 返回串的長(zhǎng)度
Locate() 找出串的一個(gè)子串
Lower() 將串轉(zhuǎn)換為小寫(xiě)
LTrim() 去掉串左邊的空格
Right() 返回串右邊的字符
RTrim() 去掉串右邊的空格
Soundex() 返回串的SOUNDEX值
SubString() 返回子串的字符
Upper() 將串轉(zhuǎn)換為大寫(xiě)
SOUNDEX
是一個(gè)將任何文 本串轉(zhuǎn)換為描述其語(yǔ)音表示的字母數(shù)字模式的算法。SOUNDEX考慮了類似 的發(fā)音字符和音節(jié),使得能對(duì)串進(jìn)行發(fā)音比較而不是字母比較。雖然 SOUNDEX不是SQL概念,但MySQL(就像多數(shù)DBMS一樣)都提供對(duì) SOUNDEX的支持。
下面給出一個(gè)使用Soundex()函數(shù)的例子。customers表中有一個(gè)顧 客Coyote Inc.,其聯(lián)系名為Y.Lee。但如果這是輸入錯(cuò)誤,此聯(lián)系名實(shí) 際應(yīng)該是Y.Lie,怎么辦?顯然,按正確的聯(lián)系名搜索不會(huì)返回?cái)?shù)據(jù)
現(xiàn)在試一下使用Soundex()函數(shù)進(jìn)行搜索,它匹配所有發(fā)音類似于 Y.Lie的聯(lián)系名:
SELECT * FROM customers WHERE Soundex(cust_contact) = Soundex('Y Lie');
11.2.2 日期和時(shí)間處理函數(shù)
AddDate() 增加一個(gè)日期(天、周等)
AddTime() 增加一個(gè)時(shí)間(時(shí)、分等)
CurDate() 返回當(dāng)前日期
CurTime() 返回當(dāng)前時(shí)間
Date() 返回日期時(shí)間的日期部分
DateDiff() 計(jì)算兩個(gè)日期之差
Date_Add() 高度靈活的日期運(yùn)算函數(shù)
Date_Format() 返回一個(gè)格式化的日期或時(shí)間串
Day() 返回一個(gè)日期的天數(shù)部分
DayOfWeek() 對(duì)于一個(gè)日期,返回對(duì)應(yīng)的星期幾
Hour() 返回一個(gè)時(shí)間的小時(shí)部分
Minute() 返回一個(gè)時(shí)間的分鐘部分
Month() 返回一個(gè)日期的月份部分
Now() 返回當(dāng)前日期和時(shí)間
Second() 返回一個(gè)時(shí)間的秒部分
Time() 返回一個(gè)日期時(shí)間的時(shí)間部分
Year() 返回一個(gè)日期的年份部分
首先需要注意的是MySQL使用的日期格式。無(wú)論你什么時(shí)候指定一個(gè)日期,不管是插入或更新表值還是用WHERE子句進(jìn)行過(guò)濾,日期必須為 格式y(tǒng)yyy-mm-dd。
因此,2005年9月1日,給出為2005-09-01。雖然其他的日期格式可能也行,但這是首選的日期格式,因?yàn)樗懦硕嗔x性(如, 04/05/06是2006年5月4日或2006年4月5日或2004年5月6日或......)
使用WHERE order_date = '2005-09-01'
可靠嗎?
order_date的數(shù)據(jù)類型為datetime,存儲(chǔ)日期及時(shí)間值。
實(shí)際中比如,存儲(chǔ)的order_date值為 2005-09-01 11:30:05,則WHERE order_date = '2005-09-01'
失敗。
解決辦法是指示MySQL僅將給出的日期與列中的日期部分進(jìn)行比 較,而不是將給出的日期與整個(gè)列值進(jìn)行比較。
為此,必須使用Date() 函數(shù)。Date(order_date) 指示MySQL僅提取列的日期部分
如果要的是日期,請(qǐng)使用Date()
如果你想要的僅是日期, 則使用Date()是一個(gè)良好的習(xí)慣,即使你知道相應(yīng)的列只包 含日期也是如此。這樣,如果由于某種原因表中以后有日期和 時(shí)間值,你的SQL代碼也不用改變。當(dāng)然,也存在一個(gè)Time() 函數(shù),在你只想要時(shí)間時(shí)應(yīng)該使用它。
Date()和Time()都是在MySQL 4.1.1中第一次引入的
如果你想檢索出2005年9月下的 所有訂單,怎么辦?
SELECT * FROM orders WHERE Year(order_date) = 2005 AND Month(order_date) = 9;
11.2.3 數(shù)值處理函數(shù)
Abs() 返回一個(gè)數(shù)的絕對(duì)值
Cos() 返回一個(gè)角度的余弦
Exp() 返回一個(gè)數(shù)的指數(shù)值
Mod() 返回除操作的余數(shù)
Pi() 返回圓周率
Rand() 返回一個(gè)隨機(jī)數(shù)
Sin() 返回一個(gè)角度的正弦
Sqrt() 返回一個(gè)數(shù)的平方根
Tan() 返回一個(gè)角度的正切
第12章 匯總數(shù)據(jù)
SQL聚集函數(shù)
AVG() 返回某列的平均值
COUNT() 返回某列的行數(shù)
MAX() 返回某列的最大值
MIN() 返回某列的最小值
SUM() 返回某列值之和
12.1.2 COUNT()函數(shù)
COUNT()函數(shù)進(jìn)行計(jì)數(shù)。可利用COUNT()確定表中行的數(shù)目或符合特
定條件的行的數(shù)目。 COUNT()函數(shù)有兩種使用方式。
- 使用COUNT(*)對(duì)表中行的數(shù)目進(jìn)行計(jì)數(shù),不管表列中包含的是空 值(NULL)還是非空值。
- 使用COUNT(column)對(duì)特定列中具有值的行進(jìn)行計(jì)數(shù),忽略 NULL值。
NULL值
如果指定列名,則指定列的值為空的行被COUNT() 函數(shù)忽略,但如果COUNT()函數(shù)中用的是星號(hào)(*),則不忽略。
12.1.3 MAX()函數(shù)
對(duì)非數(shù)值數(shù)據(jù)使用MAX()
雖然MAX()一般用來(lái)找出最大的數(shù)值或日期值,但MySQL允許將它用來(lái)返回任意列中的最大值,包括返回文本列中的最大值。
在用于文本數(shù)據(jù)時(shí),如果數(shù)據(jù)按相應(yīng)的列排序,則MAX()返回最后一行。
NULL值
MAX()函數(shù)忽略列值為NULL的行。
12.1.4 MIN()函數(shù)
對(duì)非數(shù)值數(shù)據(jù)使用MIN()
MIN()函數(shù)與MAX()函數(shù)類似,MySQL允許將它用來(lái)返回任意列中的最小值,包括返回文本列中的最小值。在用于文本數(shù)據(jù)時(shí),如果數(shù)據(jù)按相應(yīng)的列排序,則MIN()返回最前面的行。
NULL值
MIN()函數(shù)忽略列值為NULL的行。
12.2 聚集不同值
MySQL 5及后期版本
下面將要介紹的聚集函數(shù)的DISTINCT的使用,已經(jīng)被添加到MySQL 5.0.3中。下面所述內(nèi)容在MySQL 4.x中不能正常運(yùn)行。
以上5個(gè)聚集函數(shù)都可以如下使用:
- 對(duì)所有的行執(zhí)行計(jì)算,指定ALL參數(shù)或不給參數(shù)(因?yàn)锳LL是默認(rèn)
行為); - 只包含不同的值,指定DISTINCT參數(shù)。
ALL為默認(rèn)
ALL參數(shù)不需要指定,因?yàn)樗悄J(rèn)行為。如果不指定DISTINCT,則假定為ALL。
下面的例子使用AVG()函數(shù)返回特定供應(yīng)商提供的產(chǎn)品的平均價(jià)格。 它與上面的SELECT語(yǔ)句相同,但使用了DISTINCT參數(shù),因此平均值只 考慮各個(gè)不同的價(jià)格:
SELECT AVG(DISTINCT prod_price) as avg_price FROM productsWHERE vendor_id=1003;
注意
如果指定列名,則DISTINCT只能用于COUNT()。
DISTINCT不能用于COUNT(*),因此不允許使用COUNT(DISTINCT), 否則會(huì)產(chǎn)生錯(cuò)誤。類似地,DISTINCT必須使用列名,不能用于計(jì)算或表達(dá)式。
第13章 分組數(shù)據(jù)
在具體使用GROUP BY子句前,需要知道一些重要的規(guī)定。
- GROUP BY子句可以包含任意數(shù)目的列。這使得能對(duì)分組進(jìn)行嵌套, 為數(shù)據(jù)分組提供更細(xì)致的控制。
- 如果在GROUP BY子句中嵌套了分組,數(shù)據(jù)將在最后規(guī)定的分組上 進(jìn)行匯總。換句話說(shuō),在建立分組時(shí),指定的所有列都一起計(jì)算(所以不能從個(gè)別的列取回?cái)?shù)據(jù))。
- GROUP BY子句中列出的每個(gè)列都必須是檢索列或有效的表達(dá)式
(但不能是聚集函數(shù))。如果在SELECT中使用表達(dá)式,則必須在 GROUP BY子句中指定相同的表達(dá)式。不能使用別名。 - 除聚集計(jì)算語(yǔ)句外,SELECT語(yǔ)句中的每個(gè)列都必須在GROUP BY子句中給出。
- 如果分組列中具有NULL值,則NULL將作為一個(gè)分組返回。如果列 中有多行NULL值,它們將分為一組。
- GROUP BY子句必須出現(xiàn)在WHERE子句之后,ORDER BY子句之前。
13.3 過(guò)濾分組
HAVING和WHERE的差別
這里有另一種理解方法,WHERE在數(shù)據(jù)分組前進(jìn)行過(guò)濾,HAVING在數(shù)據(jù)分組后進(jìn)行過(guò)濾。這是一個(gè)重要的區(qū)別,WHERE排除的行不包括在分組中。這可能會(huì)改變計(jì)算值,從而影響HAVING子句中基于這些值過(guò)濾掉的分組。
13.4 分組和排序
雖然GROUP BY和ORDER BY經(jīng)常完成相同的工作,但它們是非常不同
的。表13-1匯總了它們之間的差別。
ORDER BY | GROUP BY |
---|---|
排序產(chǎn)生的輸出 | 分組行。但輸出可能不是分組的順序 |
任意列都可以使用(甚至非選擇的列也可以使用) | 只可能使用選擇列或表達(dá)式列,而且必須使用每個(gè)選擇列表達(dá)式 ) |
不一定需要 | 如果與聚集函數(shù)一起使用列(或表達(dá)式),則必須使用 |
第14章 使用子查詢
14.3 作為計(jì)算字段使用子查詢
使用子查詢的另一方法是創(chuàng)建計(jì)算字段。假如需要顯示customers
表中每個(gè)客戶的訂單總數(shù)。訂單與相應(yīng)的客戶ID存儲(chǔ)在orders表中。 為了執(zhí)行這個(gè)操作,遵循下面的步驟。
(1) 從customers表中檢索客戶列表。
(2) 對(duì)于檢索出的每個(gè)客戶,統(tǒng)計(jì)其在orders表中的訂單數(shù)目。
正如前兩章所述,可使用SELECT COUNT(*)對(duì)表中的行進(jìn)行計(jì)數(shù),并 且通過(guò)提供一條WHERE子句來(lái)過(guò)濾某個(gè)特定的客戶ID,可僅對(duì)該客戶的訂單進(jìn)行計(jì)數(shù)。例如,下面的代碼對(duì)客戶10001的訂單進(jìn)行計(jì)數(shù):
SELECT COUNT(*) AS orders FROM orders WHERE cust_id=1001;
為了對(duì)每個(gè)客戶執(zhí)行COUNT(*)計(jì)算,應(yīng)該將COUNT(*)作為一個(gè)子查 詢。
SELECT cust_name,
cust_state,
(SELECT COUNT(*) FROM orders WHERE orders.cust_id = customers.cust_id) AS orders
FROM customers
ORDER BY cust_name;
這條SELECT語(yǔ)句對(duì)customers表中每個(gè)客戶返回3列:
cust_name、cust_state和orders。
orders是一個(gè)計(jì)算字段,它是由圓括號(hào)中的子查詢建立的。該子查詢對(duì)檢索出的每個(gè)客戶執(zhí)行一次。在此例子中,該子查詢執(zhí)行了5次,因?yàn)闄z索出了5個(gè)客戶。
子查詢中的WHERE子句與前面使用的WHERE子句稍有不同,因?yàn)樗?用了完全限定列名(在第4章中首次提到)。下面的語(yǔ)句告訴SQL比較 orders表中的cust_id與當(dāng)前正從customers表中檢索的cust_id:
WHERE orders.cust_id = customers.cust_id
這種類型的子查詢稱為相關(guān)子查詢。任何時(shí)候只要列名可能有多義 性,就必須使用這種語(yǔ)法(表名和列名由一個(gè)句點(diǎn)分隔)。
如果不完全限制列名
SELECT cust_name,
cust_state,
(SELECT COUNT(*) FROM orders WHERE cust_id = cust_id) AS orders
FROM customers
ORDER BY cust_name;
子查詢會(huì)變成
SELECT COUNT(*) FROM orders WHERE cust_id = cust_id;
總是返回orders表中的訂單總數(shù)(因?yàn)镸ySQL查看每個(gè)訂單的cust_id 是否與本身匹配,當(dāng)然,它們總是匹配的)
不止一種解決方案
正如本章前面所述,雖然這里給出的樣 例代碼運(yùn)行良好,但它并不是解決這種數(shù)據(jù)檢索的最有效的 方法。在后面的章節(jié)中我們還要遇到這個(gè)例子。
逐漸增加子查詢來(lái)建立查詢
用子查詢測(cè)試和調(diào)試查詢很有技巧性,特別是在這些語(yǔ)句的復(fù)雜性不斷增加的情況下更是如此。用子查詢建立(和測(cè)試)查詢的最可靠的方法是逐漸進(jìn)行, 這與MySQL處理它們的方法非常相同。首先,建立和測(cè)試最 內(nèi)層的查詢。然后,用硬編碼數(shù)據(jù)建立和測(cè)試外層查詢,并且僅在確認(rèn)它正常后才嵌入子查詢。這時(shí),再次測(cè)試它。對(duì)于要 增加的每個(gè)查詢,重復(fù)這些步驟。這樣做僅給構(gòu)造查詢?cè)黾恿?一點(diǎn)點(diǎn)時(shí)間,但節(jié)省了以后(找出查詢?yōu)槭裁床徽?的大量 時(shí)間,并且極大地提高了查詢一開(kāi)始就正常工作的可能性。
第15章 鏈結(jié)表
關(guān)鍵是,相同數(shù)據(jù)出現(xiàn)多次決不是一件好事,此因素是關(guān)系數(shù)據(jù)庫(kù) 設(shè)計(jì)的基礎(chǔ)。關(guān)系表的設(shè)計(jì)就是要保證把信息分解成多個(gè)表,一類數(shù)據(jù) 一個(gè)表。各表通過(guò)某些常用的值(即關(guān)系設(shè)計(jì)中的關(guān)系(relational))互相關(guān)聯(lián)。
products表只存儲(chǔ)產(chǎn)品信息,它除了存儲(chǔ)供應(yīng)商ID(vendors表的主 鍵)外不存儲(chǔ)其他供應(yīng)商信息。vendors表的主鍵又叫作products的外鍵,它將vendors表與products表關(guān)聯(lián),利用供應(yīng)商ID能從vendors表中找出相應(yīng)供應(yīng)商的詳細(xì)信息。
維護(hù)引用完整性
重要的是,要理解聯(lián)結(jié)不是物理實(shí)體。
換句話說(shuō),它在實(shí)際的數(shù)據(jù)庫(kù)表中不存在。聯(lián)結(jié)由MySQL根據(jù)需 要建立,它存在于查詢的執(zhí)行當(dāng)中。
在使用關(guān)系表時(shí),僅在關(guān)系列中插入合法的數(shù)據(jù)非常重要。
回到這里的例子,如果在products表中插入擁有非法供應(yīng)商ID (即沒(méi)有在vendors表中出現(xiàn))的供應(yīng)商生產(chǎn)的產(chǎn)品,則這些產(chǎn)品是不可訪問(wèn)的,因?yàn)樗鼈儧](méi)有關(guān)聯(lián)到某個(gè)供應(yīng)商。
為防止這種情況發(fā)生,可指示MySQL只允許在products表的 供應(yīng)商ID列中出現(xiàn)合法值(即出現(xiàn)在vendors表中的供應(yīng)商)。 這就是維護(hù)引用完整性,它是通過(guò)在表的定義中指定主鍵和外鍵來(lái)實(shí)現(xiàn)的。(這將在第21章介紹。)
15.2 創(chuàng)建聯(lián)結(jié)
SELECT vend_name,
prod_name,
prod_price
FROM venders, products
WHERE venders.vend_id = products.vend_id
ORDER BY vend_name, prod_name;
現(xiàn)在來(lái)看FROM子句
與以前的SELECT語(yǔ)句不一樣,這條語(yǔ)句的FROM子句列出了兩個(gè)表,分別是vendors和products。它們就是這條SELECT語(yǔ)句聯(lián)結(jié)的兩個(gè)表的名字。
這兩個(gè)表用WHERE子句正確聯(lián)結(jié),WHERE子句指示MySQL匹配vendors表中的vend_id和products表中的vend_id。
15.2.1 WHERE子句的重要性
利用WHERE子句建立聯(lián)結(jié)關(guān)系似乎有點(diǎn)奇怪,但實(shí)際上,有一個(gè)很充分的理由。
請(qǐng)記住,在一條SELECT語(yǔ)句中聯(lián)結(jié)幾個(gè)表時(shí),相應(yīng)的關(guān)系是在運(yùn)行中構(gòu)造的。在數(shù)據(jù)庫(kù)表的定義中不存在能指示MySQL如何對(duì)表進(jìn)行聯(lián)結(jié)的東西。你必須自己做這件事情。
在聯(lián)結(jié)兩個(gè)表時(shí),你實(shí)際上做的是將第一個(gè)表中的每一行與第二個(gè)表中的每一行配對(duì)。WHERE子句作為過(guò)濾條件,它只包含那些匹配給定條件(這里是聯(lián)結(jié)條件)的行。
沒(méi)有WHERE子句,第一個(gè)表中的每個(gè)行將與第二個(gè)表中的每個(gè)行配對(duì),而不管它們邏輯上是否可以配在一起。
笛卡兒積(cartesian product)
由沒(méi)有聯(lián)結(jié)條件的表關(guān)系返回
的結(jié)果為笛卡兒積。檢索出的行的數(shù)目將是第一個(gè)表中的行數(shù)乘 以第二個(gè)表中的行數(shù)
15.2.2 內(nèi)部聯(lián)結(jié)
目前為止所用的聯(lián)結(jié)稱為等值聯(lián)結(jié)(equijoin),它基于兩個(gè)表之間的 相等測(cè)試。這種聯(lián)結(jié)也稱為內(nèi)部聯(lián)結(jié)。其實(shí),對(duì)于這種聯(lián)結(jié)可以使用稍 微不同的語(yǔ)法來(lái)明確指定聯(lián)結(jié)的類型。下面的SELECT語(yǔ)句返回與前面例 子完全相同的數(shù)據(jù):
SELECT vend_name,
prod_name,
prod_price
FROM venders, INNER JOIN products
ON venders.vend_id = products.vend_id
ORDER BY vend_name, prod_name;
此語(yǔ)句中的SELECT與前面的SELECT語(yǔ)句相同,但FROM子句不同。這里,兩個(gè)表之間的關(guān)系是FROM子句的組成部分,以INNER JOIN指定。在使用這種語(yǔ)法時(shí),聯(lián)結(jié)條件用特定的ON子句而不是WHERE 子句給出。傳遞給ON的實(shí)際條件與傳遞給WHERE的相同。
使用哪種語(yǔ)法
ANSI SQL規(guī)范首選INNER JOIN語(yǔ)法。此外,盡管使用WHERE子句定義聯(lián)結(jié)的確比較簡(jiǎn)單,但是使用明確的聯(lián)結(jié)語(yǔ)法能夠確保不會(huì)忘記聯(lián)結(jié)條件,有時(shí)候這樣做也能影響性能。
15.2.3 聯(lián)結(jié)多個(gè)表
SQL對(duì)一條SELECT語(yǔ)句中可以聯(lián)結(jié)的表的數(shù)目沒(méi)有限制。創(chuàng)建聯(lián)結(jié)
的基本規(guī)則也相同。首先列出所有表,然后定義表之間的關(guān)系。
第16章 創(chuàng)建高級(jí)聯(lián)結(jié)
看看下面的區(qū)別
- 子查詢
SELECT prod_id, prod_name
FROM products
WHERE vend_id= (SELECT vend_id
from products
WHERE prod_id='DTNTR')
- 自聯(lián)結(jié)
SELECT p1.prod_id, p1.prod_name
FROM products AS p1, products AS p2
WHERE p1.vend_id = p2.vend_id
AND p2.prod_id = 'DTNTR'
用自聯(lián)結(jié)而不用子查詢
自聯(lián)結(jié)通常作為外部語(yǔ)句用來(lái)替代從相同表中檢索數(shù)據(jù)時(shí)使用的子查詢語(yǔ)句。雖然最終的結(jié)果是相同的,但有時(shí)候處理聯(lián)結(jié)遠(yuǎn)比處理子查詢快得多。應(yīng)該試一下兩種方法,以確定哪一種的性能更好。
16.2.3 外部聯(lián)結(jié)
許多聯(lián)結(jié)將一個(gè)表中的行與另一個(gè)表中的行相關(guān)聯(lián)。但有時(shí)候會(huì)需
要包含沒(méi)有關(guān)聯(lián)行的那些行。例如,可能需要使用聯(lián)結(jié)來(lái)完成以下工作:
- 對(duì)每個(gè)客戶下了多少訂單進(jìn)行計(jì)數(shù),包括那些至今尚未下訂單的 客戶;
- 列出所有產(chǎn)品以及訂購(gòu)數(shù)量,包括沒(méi)有人訂購(gòu)的產(chǎn)品;
- 計(jì)算平均銷售規(guī)模,包括那些至今尚未下訂單的客戶。
在上述例子中,聯(lián)結(jié)包含了那些在相關(guān)表中沒(méi)有關(guān)聯(lián)行的行。這種 類型的聯(lián)結(jié)稱為外部聯(lián)結(jié)。
在使用OUTER JOIN
語(yǔ)法時(shí),必須使用RIGHT
或LEFT
關(guān)鍵字 指定包括其所有行的表(RIGHT指出的是OUTER JOIN右邊的表,而LEFT 指出的是OUTER JOIN左邊的表)
第17章 組合查詢
17.1 組合查詢
多數(shù)SQL查詢都只包含從一個(gè)或多個(gè)表中返回?cái)?shù)據(jù)的單條SELECT語(yǔ) 句。MySQL也允許執(zhí)行多個(gè)查詢(多條SELECT語(yǔ)句),并將結(jié)果作為單個(gè) 查詢結(jié)果集返回。這些組合查詢通常稱為并(union)或復(fù)合查詢
(compound query)。 有兩種基本情況,其中需要使用組合查詢:
- 在單個(gè)查詢中從不同的表返回類似結(jié)構(gòu)的數(shù)據(jù)
- 對(duì)單個(gè)表執(zhí)行多個(gè)查詢,按單個(gè)查詢返回?cái)?shù)據(jù)
組合查詢和多個(gè)WHERE件
多數(shù)情況下,組合相同表的兩個(gè)查詢完成的工作與具有多個(gè)WHERE子句條件的單條查詢完成的工作相同。換句話說(shuō),任何具有多個(gè)WHERE子句的SELECT語(yǔ)句都可以作為一個(gè)組合查詢給出,在以下段落中可以看到這一點(diǎn)。 這兩種技術(shù)在不同的查詢中性能也不同。因此,應(yīng)該試一下這 兩種技術(shù),以確定對(duì)特定的查詢哪一種性能更好。
SELECT vend_id, prod_id, prod_price
FROM products
WHERE prod_price < 5
UNION
SELECT vend_id, prod_id, prod_price
FROM products
WHERE vend_id in (1001, 1002)
第18章 全文本搜索
18.1 理解全文本搜索
并非所有引擎都支持全文本搜索
正如第21章所述,MySQL支持幾種基本的數(shù)據(jù)庫(kù)引擎。并非所有的引擎都支持本書(shū)所描 述的全文本搜索。兩個(gè)最常使用的引擎為MyISAM和InnoDB, 前者支持全文本搜索,而后者不支持。這就是為什么雖然本書(shū) 中創(chuàng)建的多數(shù)樣例表使用InnoDB,而有一個(gè)樣例表(productnotes表)卻使用MyISAM的原因。如果你的應(yīng)用中需要全文本搜索功能,應(yīng)該記住這一點(diǎn)。
18.2 使用全文本搜索
為了進(jìn)行全文本搜索,必須索引被搜索的列,而且要隨著數(shù)據(jù)的改 變不斷地重新索引。在對(duì)表列進(jìn)行適當(dāng)設(shè)計(jì)后,MySQL會(huì)自動(dòng)進(jìn)行所有 的索引和重新索引。
在索引之后,SELECT可與Match()和Against()一起使用以實(shí)際執(zhí)行 搜索。
第22章 視圖
第23章 存儲(chǔ)過(guò)程
迄今為止,使用的大多數(shù)SQL語(yǔ)句都是針對(duì)一個(gè)或多個(gè)表的單條語(yǔ) 句。并非所有操作都這么簡(jiǎn)單,經(jīng)常會(huì)有一個(gè)完整的操作需要多條語(yǔ)句 才能完成。例如,考慮以下的情形。
- 為了處理訂單,需要核對(duì)以保證庫(kù)存中有相應(yīng)的物品。
- 如果庫(kù)存有物品,這些物品需要預(yù)定以便不將它們?cè)儋u給別的人,并且要減少可用的物品數(shù)量以反映正確的庫(kù)存量。
- 庫(kù)存中沒(méi)有的物品需要訂購(gòu),這需要與供應(yīng)商進(jìn)行某種交互。
- 關(guān)于哪些物品入庫(kù)(并且可以立即發(fā)貨)和哪些物品退訂,需要通知相應(yīng)的客戶。
這顯然不是一個(gè)完整的例子,它甚至超出了本書(shū)中所用樣例表的范 圍,但足以幫助表達(dá)我們的意思了。執(zhí)行這個(gè)處理需要針對(duì)許多表的多條MySQL語(yǔ)句。此外,需要執(zhí)行的具體語(yǔ)句及其次序也不是固定的,它們可能會(huì)(和將)根據(jù)哪些物品在庫(kù)存中哪些不在而變化。
(不過(guò)這個(gè)存儲(chǔ)過(guò)程我們一般在業(yè)務(wù)邏輯里處理)
23.2 使用存儲(chǔ)過(guò)程的理由
優(yōu)點(diǎn)
- 通過(guò)把處理封裝在容易使用的單元中,簡(jiǎn)化復(fù)雜的操作(正如前 面例子所述)。
- 由于不要求反復(fù)建立一系列處理步驟,這保證了數(shù)據(jù)的完整性。 如果所有開(kāi)發(fā)人員和應(yīng)用程序都使用同一(試驗(yàn)和測(cè)試)存儲(chǔ)過(guò) 程,則所使用的代碼都是相同的。 這一點(diǎn)的延伸就是防止錯(cuò)誤。需要執(zhí)行的步驟越多,出錯(cuò)的可能 性就越大。防止錯(cuò)誤保證了數(shù)據(jù)的一致性。
- 簡(jiǎn)化對(duì)變動(dòng)的管理。如果表名、列名或業(yè)務(wù)邏輯(或別的內(nèi)容) 有變化,只需要更改存儲(chǔ)過(guò)程的代碼。使用它的人員甚至不需要 知道這些變化。
- 提高性能。因?yàn)槭褂么鎯?chǔ)過(guò)程比使用單獨(dú)的SQL語(yǔ)句要快。
- 存在一些只能用在單個(gè)請(qǐng)求中的MySQL元素和特性,存儲(chǔ)過(guò)程可 以使用它們來(lái)編寫(xiě)功能更強(qiáng)更靈活的代碼(在下一章的例子中可
以看到。)
換句話說(shuō),使用存儲(chǔ)過(guò)程有3個(gè)主要的好處,即簡(jiǎn)單、安全、高性能。 顯然,它們都很重要。不過(guò),在將SQL代碼轉(zhuǎn)換為存儲(chǔ)過(guò)程前,也必須知 道它的一些缺陷
缺點(diǎn)
- 一般來(lái)說(shuō),存儲(chǔ)過(guò)程的編寫(xiě)比基本SQL語(yǔ)句復(fù)雜,編寫(xiě)存儲(chǔ)過(guò)程 需要更高的技能,更豐富的經(jīng)驗(yàn)。
- 你可能沒(méi)有創(chuàng)建存儲(chǔ)過(guò)程的安全訪問(wèn)權(quán)限。許多數(shù)據(jù)庫(kù)管理員限 制存儲(chǔ)過(guò)程的創(chuàng)建權(quán)限,允許用戶使用存儲(chǔ)過(guò)程,但不允許他們 創(chuàng)建存儲(chǔ)過(guò)程。
第24章 游標(biāo)
第25章 觸發(fā)器
第26章 管理事務(wù)處理
并非所有引擎都支持事務(wù)處理
正如第21章所述,MySQL支持幾種基本的數(shù)據(jù)庫(kù)引擎。正如本章所述,并非所有引擎都支持明確的事務(wù)處理管理。MyISAM和InnoDB是兩種最常使用 的引擎。前者不支持明確的事務(wù)處理管理,而后者支持。這 就是為什么本書(shū)中使用的樣例表被創(chuàng)建來(lái)使用InnoDB而不是更經(jīng)常使用的MyISAM的原因。如果你的應(yīng)用中需要事務(wù)處理功能,則一定要使用正確的引擎類型。
26.2.1 使用ROLLBACK
SELECT * FROM ordertotals;
START TRANSACTION;
DELETE FROM ordertotals;
SELECT * FROM ordertotals;
ROLLBACK;
SELECT * FROM ordertotals;
哪些語(yǔ)句可以回退?
事務(wù)處理用來(lái)管理INSERT、UPDATE和DELETE語(yǔ)句。你不能回退SELECT語(yǔ)句。(這樣做也沒(méi)有什么意義。)你不能回退CREATE或DROP操作。事務(wù)處理塊中可以使用這兩條語(yǔ)句,但如果你執(zhí)行回退,它們不會(huì)被撤銷。
26.2.2 使用COMMIT
一般的MySQL語(yǔ)句都是直接針對(duì)數(shù)據(jù)庫(kù)表執(zhí)行和編寫(xiě)的。這就是 所謂的隱含提交(implicit commit),即提交(寫(xiě)或保存)操作是自動(dòng)進(jìn)行的。
但是,在事務(wù)處理塊中,提交不會(huì)隱含地進(jìn)行。為進(jìn)行明確的提交, 使用COMMIT語(yǔ)句。
START TRANSACTION;
DELETE FROM orderitems WHERE order_num = 20010;
DELETE FROM orders WHERE order_num = 20010;
COMMIT;
最后的COMMIT語(yǔ)句僅在不出錯(cuò)時(shí)寫(xiě)出更改。如果第一條DELETE起作用,但第二條失敗,則DELETE不會(huì)提交(實(shí)際上, 它是被自動(dòng)撤銷的)。
26.2.3 使用保留點(diǎn)
簡(jiǎn)單的ROLLBACK和COMMIT語(yǔ)句就可以寫(xiě)入或撤銷整個(gè)事務(wù)處理。但 是,只是對(duì)簡(jiǎn)單的事務(wù)處理才能這樣做,更復(fù)雜的事務(wù)處理可能需要部 分提交或回退。
SAVEPOINT delete1
每個(gè)保留點(diǎn)都可以起唯一的名字,回到指定的保留點(diǎn)
ROLLBACK TO delete1
第27章 全球化和本地化
27.1 字符集和校對(duì)順序
數(shù)據(jù)庫(kù)表被用來(lái)存儲(chǔ)和檢索數(shù)據(jù)。不同的語(yǔ)言和字符集需要以不同 的方式存儲(chǔ)和檢索。因此,MySQL需要適應(yīng)不同的字符集(不同的字母和字符),適應(yīng)不同的排序和檢索數(shù)據(jù)的方法。
在討論多種語(yǔ)言和字符集時(shí),將會(huì)遇到以下重要術(shù)語(yǔ):
- 字符集為字母和符號(hào)的集合
- 編碼為某個(gè)字符集成員的內(nèi)部表示
- 校對(duì)為規(guī)定字符如何比較的指令
校對(duì)為什么重要
排序英文正文很容易,對(duì)嗎?或許不。考慮詞APE、apex和Apple。它們處于正確的排序順序嗎?這有賴于你是否想?yún)^(qū)分大小寫(xiě)。使用區(qū)分大小寫(xiě)的校對(duì)順序,這些詞有一種排序方式,使用不區(qū)分大小寫(xiě)的校對(duì)順序有另外 一種排序方式。這不僅影響排序(如用ORDER BY排序數(shù)據(jù)),還影響搜索(例如,尋找apple的WHERE子句是否能找到 APPLE)。在使用諸如法文à或德文?這樣的字符時(shí),情況更復(fù) 雜,在使用不基于拉丁文的字符集(日文、希伯來(lái)文、俄文等)時(shí),情況更為復(fù)雜。
27.2 使用字符集和校對(duì)順序
MySQL支持眾多的字符集。為查看所支持的字符集完整列表,使用以下語(yǔ)句
SHOW CHARACTER SET;
這條語(yǔ)句顯示所有可用的字符集以及每個(gè)字符集的描述和默認(rèn)校對(duì)。
為了查看所支持校對(duì)的完整列表,使用以下語(yǔ)句:
SHOW COLLATION;
此語(yǔ)句顯示所有可用的校對(duì),以及它們適用的字符集。可以看到有的字符集具有不止一種校對(duì)。例如,latin1對(duì)不同的歐洲 語(yǔ)言有幾種校對(duì),而且許多校對(duì)出現(xiàn)兩次,一次區(qū)分大小寫(xiě)(由_cs表示),一次不區(qū)分大小寫(xiě)(由_ci表示)。
實(shí)際上,字符集很少是服務(wù)器范圍(甚至數(shù)據(jù)庫(kù)范圍)的 設(shè)置。不同的表,甚至不同的列都可能需要不同的字符集,而且兩者都可以在創(chuàng)建表時(shí)指定。
CREATE TABLE mytable(
column1 INT,
column2 VARVHAR(10),
) DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
此語(yǔ)句創(chuàng)建一個(gè)包含兩列的表,并且指定一個(gè)字符集和一個(gè)校對(duì)順序。
也可以為列單獨(dú)設(shè)置
CREATE TABLE mytable(
column1 INT,
column2 VARVHAR(10),
column3 VARVHAR(10) CHARACTER SET latin1 COLLATE latin1_general_ci
) DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
也可以在select時(shí)指定
SELECT * FROM customers
ORDER BY lastname, firstname COLLATE latin_general_cs;
臨時(shí)區(qū)分大小寫(xiě)
上面的SELECT語(yǔ)句演示了在通常不區(qū)分大小寫(xiě)的表上進(jìn)行區(qū)分大小寫(xiě)搜索的一種技術(shù)。當(dāng)然,反過(guò)來(lái)也是可以的。
最后,值得注意的是,如果絕對(duì)需要,串可以在字符集之間進(jìn)行轉(zhuǎn)換。為此,使用Cast()或Convert()函數(shù)。