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