前言
讀《sql必知必會 第四版》隨手做的筆記,寫的比較亂,可讀性并不好,讀的是中文版,翻譯過來的感覺有點怪怪的。
想要pdf的話可以留郵箱給我。
1. 使用DISTINCT關鍵字,它指示數據庫只返回不同的值。
SELECT DISTINCT vend_id FROM Products;
警告:不能部分使用DISTINCT
DISTINCT關鍵字作用于所有的列,不僅僅是跟在其后的那一列。例如,你指定SELECT DISTINCT vend_id, prod_price,除非指定的兩列完 全相同,否則所有的行都會被檢索出來。
2. LIMIT
如果你只想返回第一行或者一定數量的行
-- LIMIT 5指示MySQL等DBMS返回不超過5行的數據
SELECT prod_name FROM Products LIMIT 5;
-- LIMIT 5 OFFSET 5指示MySQL等DBMS返回從第5行起的5行數據。
SELECT prod_name FROM Products LIMIT 5 OFFSET 5;
3. 排序檢索數據
1. ORDER BY,根據需要排序檢索出的數據。
SELECT prod_name FROM Products
ORDER BY prod_name;
ORDER BY子句的位置
在指定一條ORDER BY子句時,應該保證它是SELECT語句中最后一條子
句。如果它不是最后的子句,將會出現錯誤消息。
2. 按多個列排序
要按多個列排序,簡單指定列名,列名之間用逗號分開即可(就像選擇多個列
時那樣)。
--下面的代碼檢索3個列,并按其中兩個列對結果進行排序——首先按價格,
然后按名稱排序。
SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY prod_price, prod_name;
重要的是理解在按多個列排序時,排序的順序完全按規定進行。換句話說,對于上述例子中的輸出,僅在多個行具有相同的prod_price值時才 對產品按prod_name進行排序。如果prod_price列中所有的值都是唯一的,則不會按prod_name排序。
3. 按列位置排序
除了能用列名指出排序順序外,ORDER BY還支持按相對列位置進行排序。
```sql
-- 只對 2, 3 列進行排序
SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY 2, 3;
```
4. 指定排序方向
排序默認升序,降序的話用 DESC 關鍵字
SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY prod_price DESC;
如果打算用多個列排序
-- DESC關鍵字只應用到直接位于其前面的列名。
-- 在上例中,只對prod_price列指定DESC,對prod_name列不指定。
-- 因此,prod_price列以降序排 序,而prod_name列(在每個價格內)仍然按標準的升序排序。
SELECT prod_id, prod_price, prod_name FROM Products
ORDER BY prod_price DESC, prod_name;
警告:在多個列上降序排序 如果想在多個列上進行降序排序,必須對每一列指定DESC關鍵字。
DESC是DESCENDING的縮寫,這兩個關鍵字都可以使用。與DESC相對的是ASC(或ASCENDING),在升序排序時可以指定它。但實際 上,ASC沒有多大用處,因為升序是默認的(如果既不指定ASC也不指定DESC,則假定為ASC)
4. 過濾數據
1. 使用WHERE子句
數據根據WHERE子句中指定的搜索條件進行過濾。WHERE子句在表名(FROM子句)之后給出。
SELECT prod_name, prod_price FROM Products
WHERE prod_price = 3.49;
警告:WHERE子句的位置
在同時使用ORDER BY和WHERE子句時,應該讓ORDER BY位于WHERE之后,否則將會產生錯誤
2. WHERE子句操作符
操作符 | 說明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
! | 不小于 |
> | 大于 |
>= | 大于等于 |
!> | 不大于 |
BETWEEN | 在指定的兩個值之間 |
IS NULL | 為NULL值 |
-- 列出所有不是供應商DLL01制造的產品
SELECT vend_id, prod_name FROM Products
WHERE vend_id <> 'DLL01';
--BETWEEN操作符可用來檢索價格在5美元和10美元之間的所有產品
SELECT prod_name, prod_price
FROM Products
WHERE prod_price BETWEEN 5 AND 10;
-- 檢索出 prod_price 是空的字段
SELECT prod_name
FROM Products
WHERE prod_price IS NULL;
5. 高級數據過濾
NOT和IN
1. 組合WHERE子句
1.1. AND操作符
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;
1.2. OR操作符
OR操作符與AND操作符正好相反,它指示DBMS檢索匹配任一條件的行。事實上,許多DBMS在OR WHERE子句的第一個條件得到滿足的情況下,
就不再計算第二個條件了(在第一個條件滿足時,不管第二個條件是否滿足,相應的行都將被檢索出來)
SELECT prod_name, prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = ‘BRS01’;
1.3. 求值順序
SQL(像多數語言一樣)在處理OR操作符前,優先處理AND操作符。
SELECT prod_name, prod_price FROM Products
WHERE vend_id = 'DLL01' OR vend_id = ‘BRS01’ AND prod_price >= 10;
提示:在WHERE子句中使用圓括號 任何時候使用具有AND和OR操作符的WHERE子句,都應該使用圓括號明確地分組操作符。不要過分依賴默認求值順序,即使它確實如你希望的 那樣。使用圓括號沒有什么壞處,它能消除歧義。
2. IN操作符
IN操作符用來指定條件范圍,范圍中的每個條件都可以進行匹配。IN取一組由逗號分隔、括在圓括號中的合法值。
SELECT prod_name, prod_price
FROM Products
WHERE vend_id IN ( 'DLL01', 'BRS01' ) ORDER BY prod_name;
--類似與
SELECT prod_name, prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01' ORDER BY prod_name;
3. NOT操作符
WHERE子句中的NOT操作符有且只有一個功能,那就是否定其后所跟的任何條件。因為NOT從不單獨使用(它總是與其他操作符一起使用),所以
它的語法與其他操作符有所不同。NOT關鍵字可以用在要過濾的列前,而不僅是在其后。
NOT WHERE子句中用來否定其后條件的關鍵字。
SELECT prod_name
FROM Products
WHERE NOT vend_id = 'DLL01'
ORDER BY prod_name;
- 等同于
SELECT prod_name
FROM Products
WHERE vend_id <> 'DLL01'
ORDER BY prod_name;
6. 用通配符進行過濾
為在搜索子句中使用通配符,必須使用LIKE操作符。LIKE指 示DBMS,后跟的搜索模式利用通配符匹配而不是簡單的相等匹配進行比較。
通配符搜索只能用于文本字段(串),非文本數據類型字段不能使用通配符搜索。
1. 百分號(%)通配符
%表示任何字符出現任意次數
--找出所有以詞Fish起頭的產品
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';
通配符可在搜索模式中的任意位置使用,并且可以使用多個通配符。下面的例子使用兩個通配符,它們位于模式的兩端:
--'%bean bag%'表示匹配任何位置上包含文本bean bag的值
--不論它之前或之后出現什么字符
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '%bean bag%';
2. 下劃線(_)通配符
下劃線的用途與%一樣,但它只匹配單個字符,而不是多個字符
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear';
與%能匹配0個字符不同,_總是剛好匹配一個字符,不能多也不能少。
3. 方括號([ ])通配符
方括號([])通配符用來指定一個字符集,它必須匹配指定位置(通配符的位置)的一個字符
-- 找出所有名字以J或M起頭的聯系人
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;
--查詢匹配不以J或M起頭的任意聯系人名
--如果使用的是Microsoft Access,需要用!而不是^來否定一個集合
--因此,使用的是[!JM]而不是[^JM]
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[^JM]%'
ORDER BY cust_contact;
--也可以使用NOT操作符得出相同的結果
SELECT cust_contact
FROM Customers
WHERE NOT cust_contact LIKE '[JM]%'
ORDER BY cust_contact;
7. 創建計算字段
1. 拼接字段
拼接(concatenate)
將值聯結到一起(將一個值附加到另一個值)構成單個值。
Access和SQL Server使用+號。
DB2、Oracle、PostgreSQL、SQLite和Open Office Base使用||。
SELECT vend_name || ' (' || vend_country || ')'
FROM Vendors
ORDER BY vend_name;
/*
*再看看上述SELECT語句返回的輸出。
*結合成一個計算字段的兩個列用空格填充。
*許多數據庫(不是所有)保存填充為列寬的文本值,而實際上
*你要的結果不需要這些空格。
*為正確返回格式化的數據,必須去掉這些空格。
*這可以使用SQL的RTRIM()函數來完成
*/
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
FROM Vendors
ORDER BY vend_name;
說明:TRIM函數 大多數DBMS都支持RTRIM()(正如剛才所見,它去掉字符串右邊的空格)、LTRIM()(去掉字符串左邊的空格)以及TRIM()(去掉字符串左 右兩邊的空格)。
2. 使用別名
別名(alias)是一個字段或值的替換名。別名用AS關鍵字賦予
--它指示SQL創建一個包含指定計算結果的名 為vend_title的計算字段。
--任何客戶端應用都可以按名稱引用這個列,就像 它是一個實際的表列一樣。
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
AS vend_title
FROM Vendors
ORDER BY vend_name;
3. 執行算術計算
--檢索訂單號20008中的所有物品
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;
SQL算術操作符 +、-、*、/
提示:如何測試計算
SELECT語句為測試、檢驗函數和計算提供了很好的方法。雖然SELECT通常用于從表中檢索數據,但是省略了FROM子句后就是簡單地訪問和處理表達式,例如SELECT 3 * 2;將返回6,SELECT Trim(' abc ');將返回abc,SELECT Now();使用Now()函數返回當前日期和時間。現在你明 白了,可以根據需要使用SELECT語句進行檢驗。
8. 使用數據處理函數
函 數 | 語 法 |
---|---|
提取字符串的組成部分 | Access使用MID();DB2、Oracle、PostgreSQL和SQLite使用SUBSTR();MySQL和SQL Server使用SUBSTRING() |
數據類型轉換 | Access和Oracle使用多個函數,每種類型的轉換有一個函數;DB2和PostgreSQL使用CAST();MariaDB、MySQL和SQL Server使用 CONVERT() |
取當前日期 | Access使用NOW();DB2和PostgreSQL使用CURRENT_DATE;MariaDB和MySQL使用CURDATE();Oracle使用SYSDATE;SQL Server 使用GETDATE();SQLite使用DATE() |
與SQL語句不一樣,SQL函數不是可移植的。這表示為特定SQL實現編寫的代碼在其他實現中可能不正常.
例如:
函 數 | 語 法 |
---|---|
提取字符串的組成部分 | Access使用MID();DB2、Oracle、PostgreSQL和SQLite使用SUBSTR();MySQL和SQL Server使用SUBSTRING() |
數據類型轉換 | Access和Oracle使用多個函數,每種類型的轉換有一個函數;DB2和PostgreSQL使用CAST();MariaDB、MySQL和SQL Server使用 CONVERT() |
取當前日期 | Access使用NOW();DB2和PostgreSQL使用CURRENT_DATE;MariaDB和MySQL使用CURDATE();Oracle使用SYSDATE;SQL Server 使用GETDATE();SQLite使用DATE() |
1. 文本處理函數
-- UPPER()將文本轉換為大寫
SELECT vend_name, UPPER(vend_name) AS vend_name_upcase
FROM Vendors
ORDER BY vend_name;
常用的文本處理函數
函 數 | 說 明 |
---|---|
LEFT()(或使用子字符串函數) | 返回字符串左邊的字符 |
LENGTH()(也使用DATALENGTH()或LEN()) | 返回字符串的長度 |
LOWER()(Access使用LCASE()) | 將字符串轉換為小寫 |
LTRIM() | 去掉字符串左邊的空格 |
RIGHT()(或使用子字符串函數) | 返回字符串右邊的字符 |
RTRIM() | 去掉字符串右邊的空格 |
SOUNDEX() | 返回字符串的SOUNDEX值 |
UPPER()(Access使用UCASE()) | 將字符串轉換為大寫 |
SOUNDEX是一個將任何文本串轉換為描述其語音表示的字母數字模式的算法。SOUNDEX考慮了類似的發 音字符和音節,使得能對字符串進行發音比較而不是字母比較。
說明:SOUNDEX支持
Microsoft Access和PostgreSQL不支持SOUNDEX(),因此以下的例子不適用于這些DBMS。 另外,如果在創建SQLite時使用了SQLITE_SOUNDEX編譯時選項,那么SOUNDEX()在SQLite中就可用。因為SQLITE_SOUNDEX不是默認的編譯時
選項,所以多數SQLite實現不支持SOUNDEX()。
-- WHERE子句使用SOUNDEX()函數把cust_contact列值和搜索字符串轉換為它們的SOUNDEX值。
--因為Michael Green和Michelle Green發音相似,所以它們的SOUNDEX值匹配
--因此WHERE子句正確地過濾出了所需的數據。
SELECT cust_name, cust_contact
FROM Customers
WHERE SOUNDEX(cust_contact) = SOUNDEX('Michael Green');
2. 日期和時間處理函數
--SQL Server中檢索2012年的所有訂單
SELECT order_num
FROM Orders
WHERE DATEPART(yy, order_date) = 2012;
-- SQLite
SELECT order_num
FROM Orders
WHERE strftime('%Y', order_date) = 2012;
3. 數值處理函數
函 數 | 說 明 |
---|---|
ABS() | 返回一個數的絕對值 |
COS() | 返回一個角度的余弦 |
EXP() | 返回一個數的指數值 |
PI() | 返回圓周率 |
SIN() | 返回一個角度的正弦 |
SQRT() | 返回一個數的平方根 |
TAN() | 返回一個角度的正切 |
常用數值處理函數
函 數 | 說 明 |
---|---|
ABS() | 返回一個數的絕對值 |
COS() | 返回一個角度的余弦 |
EXP() | 返回一個數的指數值 |
PI() | 返回圓周率 |
SIN() | 返回一個角度的正弦 |
SQRT() | 返回一個數的平方根 |
TAN() | 返回一個角度的正切 |
9. 匯總數據
1. 聚集函數
聚集函數(aggregate function) 對某些行運行的函數,計算并返回一個值。
SQL聚集函數
函 數 | 說 明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行數 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值之和 |
1.1. AVG()函數
AVG()通過對表中行數計數并計算其列值之和,求得該列的平均值。AVG()可用來返回所有列的平均值,也可以用來返回特定列或行的平均值。
--返回值avg_price,它包含Products表中所有產品的平均價格
SELECT AVG(prod_price) AS avg_price
FROM Products;
SELECT AVG(prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';
警告:只用于單個列
AVG()只能用來確定特定數值列的平均值,而且列名必須作為函數參數給出。為了獲得多個列的平均值,必須使用多個AVG()函數。
說明:NULL值
AVG()函數忽略列值為NULL的行。
1.2. COUNT()函數
COUNT()函數進行計數。可利用COUNT()確定表中行的數目或符合特定條件的行的數目。
COUNT()函數有兩種使用方式:
- 使用COUNT(*)對表中行的數目進行計數,不管表列中包含的是空值(NULL)還是非空值。
- 使用COUNT(column)對特定列中具有值的行進行計數,忽略NULL值。
SELECT COUNT(*) AS num_cust
FROM Customers;
--只對具有電子郵件地址的客戶計數:
SELECT COUNT(cust_email) AS num_cust
FROM Customers;
1.3 MAX()函數
MAX()返回指定列中的最大值。MAX()要求指定列名
SELECT MAX(prod_price) AS max_price
FROM Products;
1.4 MIN()函數
返回指定列的最小值,要求指定列名
SELECT MIN(prod_price) AS min_price
FROM Products;
1.5 SUM()函數
SUM()用來返回指定列值的和(總計)
SELECT SUM(quantity) AS items_ordered
FROM OrderItems
WHERE order_num = 20005;
-- SUM()也可以用來合計計算值
SELECT SUM(item_price*quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;
2. 聚集不同值
以上5個聚集函數都可以如下使用:
- 對所有行執行計算,指定ALL參數或不指定參數(因為ALL是默認行為)。
- 只包含不同的值,指定DISTINCT參數。
--在使用了DISTINCT后,此例子中的avg_price比較高
--因為有多個物品具有相同的較低價格。
--排除它們提升了平均價格
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';
警告:DISTINCT不能用于COUNT()
如果指定列名,則DISTINCT只能用于COUNT()。DISTINCT不能用于COUNT()。類似地,DISTINCT必須使用列名,不能用于計算或表達式。
提示:將DISTINCT用于MIN()和MAX()
雖然DISTINCT從技術上可用于MIN()和MAX(),但這樣做實際上沒有價值。一個列中的最小值和最大值不管是否只考慮不同值,結果都是相同
的。
說明:其他聚集參數
除了這里介紹的DISTINCT和ALL參數,有的DBMS還支持其他參數,如支持對查詢結果的子集進行計算的TOP和TOP PERCENT。為了解具體的 DBMS支持哪些參數,請參閱相應的文檔。
3. 組合聚集函數
-- 這里用單條SELECT語句執行了4個聚集計算
--返回4個值(Products表中物品的數目
--產品價格的最高值、最低值以及平均值)
SELECT COUNT(*) AS num_items,
MIN(prod_price) AS price_min,
MAX(prod_price) AS price_max,
AVG(prod_price) AS price_avg
FROM Products;
10. 分組數據
介紹如何分組數據,以便匯總表內容的子集。這涉及兩個新SELECT語句子句:GROUP BY子句和HAVING子句
1. 創建分組
-- 分組是使用SELECT語句的GROUP BY子句建立的。
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
在使用GROUP BY子句前,需要知道一些重要的規定。
- GROUP BY子句可以包含任意數目的列,因而可以對分組進行嵌套,更細致地進行數據分組。
- 如果在GROUP BY子句中嵌套了分組,數據將在最后指定的分組上進行匯總。換句話說,在建立分組時,指定的所有列都一起計算(所以不 能從個別的列取回數據)。
- GROUP BY子句中列出的每一列都必須是檢索列或有效的表達式(但不能是聚集函數)。如果在SELECT中使用表達式,則必須在GROUP BY子 句中指定相同的表達式。不能使用別名。
- 大多數SQL實現不允許GROUP BY列帶有長度可變的數據類型(如文本或備注型字段)。
- 除聚集計算語句外,SELECT語句中的每一列都必須在GROUP BY子句中給出。
- 如果分組列中包含具有NULL值的行,則NULL將作為一個分組返回。如果列中有多行NULL值,它們將分為一組。
- GROUP BY子句必須出現在WHERE子句之后,ORDER BY子句之前。
2. 過濾分組
除了能用GROUP BY分組數據外,SQL還允許過濾分組,規定包括哪些分組,排除哪些分組。
WHERE過濾行,而HAVING過濾分組。
SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;
說明:使用HAVING和WHERE
HAVING與WHERE非常類似,如果不指定GROUP BY,則大多數DBMS會同等對待它們。不過,你自己要能區分這一點。使用HAVING時應該結
合GROUP BY子句,而WHERE子句用于標準的行級過濾。
3. 分組和排序
ORDER BY | GROUP BY |
---|---|
對產生的輸出排序 | 對行分組,但輸出可能不是分組的順序 |
任意列都可以使用(甚至非選擇的列也可以使用) | 只可能使用選擇列或表達式列,而且必須使用每個選擇列表達式 |
不一定需要 | 如果與聚集函數一起使用列(或表達式),則必須使用 |
GROUP BY和ORDER BY經常完成相同的工作,但它們非常不同
ORDER BY與GROUP BY
ORDER BY | GROUP BY |
---|---|
對產生的輸出排序 | 對行分組,但輸出可能不是分組的順序 |
任意列都可以使用(甚至非選擇的列也可以使用) | 只可能使用選擇列或表達式列,而且必須使用每個選擇列表達式 |
不一定需要 | 如果與聚集函數一起使用列(或表達式),則必須使用 |
提示:不要忘記ORDER BY
一般在使用GROUP BY子句時,應該也給出ORDER BY子句。這是保證數據正確排序的唯一方法。千萬不要僅依賴GROUP BY排序數據。
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items, order_num;
4. SELECT子句順序
子 句 | 說 明 | 是否必須使用 |
---|---|---|
SELECT | 要返回的列或表達式 | 是 |
FROM | 從中檢索數據的表 | 僅在從表選擇數據時使用 |
WHERE | 行級過濾 | 否 |
GROUP BY | 分組說明 | 僅在按組計算聚集時使用 |
HAVING | 組級過濾 | 否 |
ORDER BY | 輸出排序順序 | 否 |
11. 使用子查詢
1. 子查詢
-- 語句1
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';
-- 語句2
SELECT cust_id
FROM Orders
WHERE order_num IN (20007,20008);
-- 結合 變為子查詢
-- 在SELECT語句中,子查詢總是從內向外處理。
-- DBMS實際上執行了兩個操作。
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');
警告:只能是單列
作為子查詢的SELECT語句只能查詢單個列。企圖檢索多個列將返回錯誤
警告:子查詢和性能
這里給出的代碼有效,并且獲得了所需的結果。但是,使用子查詢并不總是執行這類數據檢索的最有效方法
2. 作為計算字段使用子查詢
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;
12. 聯結表
1. 聯結
1.1. 關系表
1.2. 建立連接
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
內聯接
SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
-- 連接多個表
SELECT prod_name, vend_name, prod_price, quantity
FROM OrderItems, Products, Vendors
WHERE Products.vend_id = Vendors.vend_id
AND OrderItems.prod_id = Products.prod_id
AND order_num = 20007;
警告:性能考慮
DBMS在運行時關聯指定的每個表,以處理聯結。這種處理可能非常耗費資源,因此應該注意,不要聯結不必要的表。聯結的表越多,性能下降越厲害。
13. 創建高級聯結
使用表別名
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';
內聯結和外聯結
INNER JOIN、 OUTTER JOIN
警告:SQLite外聯結
SQLite支持LEFT OUTER JOIN,但不支持RIGHT OUTER JOIN。幸好,如果你確實需要在SQLite中使用RIGHT OUTER JOIN,有一種更簡單的辦 法,這將在下面的提示中介紹。
提示:外聯結的類型
要記住,總是有兩種基本的外聯結形式:左外聯結和右外聯結。它們之間的唯一差別是所關聯的表的順序。換句話說,調整FROM或WHERE子
句中表的順序,左外聯結可以轉換為右外聯結。因此,這兩種外聯結可以互換使用,哪個方便就用哪個。
3. 使用帶聚集函數的聯結
--檢索所有顧客及每個顧客所下的訂單數
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
14. 組合查詢
利用UNION操作符將多條SELECT語句組合成一個結果集。
使用UNION很簡單,所要做的只是給出每條SELECT語句,在各條語句之間放上關鍵字UNION。
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';
UNION規則
可以看到,UNION非常容易使用,但在進行組合時需要注意幾條規則。
- UNION必須由兩條或兩條以上的SELECT語句組成,語句之間用關鍵字UNION分隔(因此,如果組合四條SELECT語句,將要使用三個UNION關鍵字)。
- UNION中的每個查詢必須包含相同的列、表達式或聚集函數(不過,各個列不需要以相同的次序列出)。
- 列數據類型必須兼容:類型不必完全相同,但必須是DBMS可以隱含轉換的類型(例如,不同的數值類型或不同的日期類型)。
1. 包含或取消重復的行
使用UNION時,重復的行會被自動取消。
這是UNION的默認行為,如果愿意也可以改變它。事實上,如果想返回所有的匹配行,可使用UNION ALL而不是UNION。
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
對組合查詢結果排序
SELECT語句的輸出用ORDER BY子句排序。在用UNION組合查詢時,只能使用一條ORDER BY子句,它必須位于最后一條SELECT語句之后。對于結果
集,不存在用一種方式排序一部分,而又用另一種方式排序另一部分的情況,因此不允許使用多條ORDER BY子句。
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'
ORDER BY cust_name, cust_contact;
15. 插入數據
INSERT
- 數據插入
INSERT用來將行插入(或添加)到數據庫表。插入有幾種方式:
- 插入完整的行;
- 插入行的一部分;
- 插入某些查詢的結果。
插入完整的行
把數據插入表中的最簡單方法是使用基本的INSERT語法,它要求指定表名和插入到新行中的值。
INSERT INTO Customers
VALUES('1000000006',
'Toy Land',
'123 Any Street',
'New York',
'NY',
'11111',
'USA',
NULL,
NULL);
提示:INTO關鍵字
在某些SQL實現中,跟在INSERT之后的INTO關鍵字是可選的。但是,即使不一定需要,最好還是提供這個關鍵字,這樣做將保證SQL代碼在 DBMS之間可移植。
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);
警告:小心使用VALUES
不管使用哪種INSERT語法,VALUES的數目都必須正確。如果不提供列名,則必須給每個表列提供一個值;如果提供列名,則必須給列出的每個列一個值。否則,就會產生一條錯誤消息,相應的行不能成功插入。
插入部分行
INSERT INTO Customers(cust_id,
cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
VALUES('1000000006',
'Toy Land',
'123 Any Street',
'New York',
'NY',
'11111',
'USA');
警告:省略列
如果表的定義允許,則可以在INSERT操作中省略某些列。省略的列必須滿足以下某個條件。
- 該列定義為允許NULL值(無值或空值)。
- 在表定義中給出默認值。這表示如果不給出值,將使用默認值。
如果對表中不允許NULL值且沒有默認值的列不給出值,DBMS將產生錯誤消息,并且相應的行插入不成功。
插入檢索出的數據
/**
*使用INSERT SELECT從CustNew中將所有數據導入Customers。
*SELECT語句從CustNew檢索出要插入的值,而不是列出它們。
*SELECT中列 出的每一列對應于Customers表名后所跟的每一列。
*這條語句將插入多少行呢?這依賴于CustNew表有多少行。
*如果這個表為空,則沒有行被插 入(也不產生錯誤,因為操作仍然是合法的)。
*如果這個表確實有數據,則所有數據將被插入到Customers。
**/
INSERT
INTO Customers(cust_id,cust_contact,
cust_email,
cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
SELECT
cust_id,
cust_contact,
cust_email,
cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country
FROM CustNew;
提示:INSERT SELECT中的列名
為簡單起見,這個例子在INSERT和SELECT語句中使用了相同的列名。但是,不一定要求列名匹配。事實上,DBMS一點兒也不關心SELECT返 回的列名。它使用的是列的位置,因此SELECT中的第一列(不管其列名)將用來填充表列中指定的第一列,第二列將用來填充表列中指定的 第二列,如此等等。
INSERT SELECT中SELECT語句可以包含WHERE子句,以過濾插入的數據。
提示:插入多行
INSERT通常只插入一行。要插入多行,必須執行多個INSERT語句。INSERT SELECT是個例外,它可以用一條INSERT插入多行,不管SELECT語句返回多少行,都將被INSERT插入。
2. 從一個表復制到另一個表
有一種數據插入不使用INSERT語句。要將一個表的內容復制到一個全新的表(運行中創建的表),可以使用SELECT INTO語句。
-- 這條SELECT語句創建一個名為CustCopy的新表,
-- 并把Customers表的整個內容復制到新表中。
-- 因為這里使用的是SELECT *,
-- 所以將在CustCopy表 中創建(并填充)與Customers表的每一列相同的列。
-- 要想只復制部分的列,可以明確給出列名,而不是使用*通配符。
SELECT *
INTO CustCopy
FROM Customers;
-- MariaDB、MySQL、Oracle、PostgreSQL和SQLite使用的語法稍有不同
CREATE TABLE CustCopy AS
SELECT * FROM Customers;
在使用SELECT INTO時,需要知道一些事情:
- 任何SELECT選項和子句都可以使用,包括WHERE和GROUP BY;
- 可利用聯結從多個表插入數據;
- 不管從多少個表中檢索數據,數據都只能插入到一個表中。
16. 更新和刪除數據
UPDATE和DELETE
有兩種使用UPDATE的方式:
- 更新表中的特定行;
- 更新表中的所有行。
基本的UPDATE語句由三部分組成,分別是:
- 要更新的表;
- 列名和它們的新值;
- 確定要更新哪些行的過濾條件。
UPDATE Customers
SET cust_email = 'kim@thetoystore.com'
WHERE cust_id = '1000000005';
-- 多個列
UPDATE Customers
SET cust_contact = 'Sam Roberts',
cust_email = 'sam@toyland.com'
WHERE cust_id = '1000000006';
提示:FROM關鍵字
有的SQL實現支持在UPDATE語句中使用FROM子句,用一個表的數據更新另一個表的行。如想知道你的DBMS是否支持這個特性,請參閱它的 文檔。
-- 要刪除某個列的值,可設置它為NULL(假如表定義允許NULL值)
UPDATE Customers
SET cust_email = NULL
WHERE cust_id = '1000000005';
2. 刪除數據
使用DELETE語句。有兩種使用DELETE的方式:
- 從表中刪除特定的行;
- 從表中刪除所有行。
DELETE FROM Customers
WHERE cust_id = '1000000006';
DELETE不需要列名或通配符。DELETE刪除整行而不是刪除列。要刪除指定的列,請使用UPDATE語句。
說明:刪除表的內容而不是表
DELETE語句從表中刪除行,甚至是刪除表中所有行。但是,DELETE不刪除表本身
17. 創建和操縱表
利用CREATE TABLE創建表,必須給出下列信息:
- 新表的名字,在關鍵字CREATE TABLE之后給出;
- 表列的名字和定義,用逗號分隔;
- 有的DBMS還要求指定表的位置。
CREATE TABLE Products
(
prod_id CHAR(10) NOT NULL,
vend_id CHAR(10) NOT NULL,
prod_name CHAR(254) NOT NULL,
prod_price DECIMAL(8,2) NOT NULL,
prod_desc VARCHAR(1000) NULL
);
指定默認值
-- SQL允許指定默認值,在插入行時如果不給出值,
-- DBMS將自動采用默認值。
-- 默認值在CREATE TABLE語句的列定義中用關鍵字DEFAULT指定。
CREATE TABLE OrderItems
(
order_num INTEGER NOT NULL,
order_item INTEGER NOT NULL,
prod_id CHAR(10) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
item_price DECIMAL(8,2) NOT NULL,
);
獲得系統日期
DBMS | 函數/變量 |
---|---|
Access | NOW() |
DB2 | CURRENT_DATE |
MySQL | CURRENT_DATE() |
Oracle | SYSDATE |
PostgreSQL | CURRENT_DATE |
SQL Server | GETDATE() |
SQLite | date('now') |
2. 更新表
更新表定義,可以使用ALTER TABLE語句
以下是使用ALTERTABLE時需要考慮的事情。
- 理想情況下,不要在表中包含數據時對其進行更新。應該在表的設計過程中充分考慮未來可能的需求,避免今后對表的結構做大改動。
- 所有的DBMS都允許給現有的表增加列,不過對所增加列的數據類型(以及NULL和DEFAULT的使用)有所限制。
- 許多DBMS不允許刪除或更改表中的列。
- 多數DBMS允許重新命名表中的列。
- 許多DBMS限制對已經填有數據的列進行更改,對未填有數據的列幾乎沒有限制。
-- 給Vendors表增加一個名為vend_phone的列,其數據類型為CHAR。
ALTER TABLE Vendors
ADD vend_phone CHAR(20);
ALTER TABLE Vendors
DROP COLUMN vend_phone;
說明:ALTER TABLE和SQLite
SQLite對使用ALTER TABLE執行的操作有所限制。最重要的一個限制是,它不支持使用ALTER TABLE定義主鍵和外鍵,這些必須在最初創建表 時指定。
3 刪除表
DROP TABLE CustCopy;
4 重命名表
每個DBMS對表重命名的支持有所不同。對于這個操作,不存在嚴格的標準。DB2、MariaDB、MySQL、Oracle和PostgreSQL用戶使
用RENAME語句,SQL Server用戶使用sp_rename存儲過程,SQLite用戶使用ALTER TABLE語句。
18. 使用視圖
SELECT cust_name, cust_contact
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num
AND prod_id = 'RGAN01';
-- 假如可以把整個查詢包裝成一個名為ProductCustomers的虛擬表,
-- 則可以如下輕松地檢索出相同的數據:
SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';
視圖創建和使用的一些最常見的規則和限制。
- 與表一樣,視圖必須唯一命名(不能給視圖取與別的視圖或表相同的名字)。
- 對于可以創建的視圖數目沒有限制。
創建視圖,必須具有足夠的訪問權限。這些權限通常由數據庫管理人員授予。 - 視圖可以嵌套,即可以利用從其他視圖中檢索數據的查詢來構造視圖。所允許的嵌套層數在不同的DBMS中有所不同(嵌套視圖可能會嚴 重降低查詢的性能,因此在產品環境中使用之前,應該對其進行全面測試)。
- 許多DBMS禁止在視圖查詢中使用ORDER BY子句。
- 有些DBMS要求對返回的所有列進行命名,如果列是計算字段,則需要使用別名(關于列別名的更多信息,請參閱第7課)。
- 視圖不能索引,也不能有關聯的觸發器或默認值。
- 有些DBMS把視圖作為只讀的查詢,這表示可以從視圖檢索數據,但不能將數據寫回底層表。詳情請參閱具體的DBMS文檔。
- 有些DBMS允許創建這樣的視圖,它不能進行導致行不再屬于視圖的插入或更新。例如有一個視圖,只檢索帶有電子郵件地址的顧客。如 果更新某個顧客,刪除他的電子郵件地址,將使該顧客不再屬于視圖。這是默認行為,而且是允許的,但有的DBMS可能會防止這種情況 發生。
1. 創建視圖
視圖用CREATE VIEW語句來創建。與CREATE TABLE一樣,CREATE VIEW只能用于創建不存在的視圖。
說明:視圖重命名
刪除視圖,可以使用DROP語句,其語法為DROP VIEW viewname;覆蓋(或更新)視圖,必須先刪除它,然后再重新創建。
-- 這條語句創建一個名為ProductCustomers的視圖,它聯結三個表,返回已訂購了任意產品的所有顧客的列表。
-- 如果執行SELECT * FROM ProductCustomers,將列出訂購了任意產品的顧客。
CREATE VIEW ProductCustomers AS
SELECT cust_name, cust_contact, prod_id
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num;
-- 檢索訂購了產品RGAN01的顧客
SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';
19. 使用存儲過程
什么要使用存儲過程,如何使用存儲過程,以及創建和使用存儲過程的基本語法。
SqlLite 不支持,而接下來又主要用到 SqlLite 故不做筆記
20. 管理事務處理
介紹什么是事務處理,如何利用COMMIT和ROLLBACK語句管理事務處理。
- 事務處理
使用事務處理(transaction processing),通過確保成批的SQL操作要么完全執行,要么完全不執行,來維護數據庫的完整性。
關于事務處理需要知道的幾個術語:
- 事務(transaction)指一組SQL語句;
- 回退(rollback)指撤銷指定SQL語句的過程;
- 提交(commit)指將未存儲的SQL語句結果寫入數據庫表;
- 保留點(savepoint)指事務處理中設置的臨時占位符(placeholder),可以對它發布回退(與回退整個事務處理不同)。
提示:可以回退哪些語句?
事務處理用來管理INSERT、UPDATE和DELETE語句。不能回退SELECT語句(回退SELECT語句也沒有必要),也不能回退CREATE或DROP操作。事 務處理中可以使用這些語句,但進行回退時,這些操作也不撤銷。
2. 控制事務處理
管理事務的關鍵在于將SQL語句組分解為邏輯塊,并明確規定數據何時應該回退,何時不應該回退。
-- SQL Server
-- BEGIN TRANSACTION和COMMIT TRANSACTION語句之間的
-- SQL必須完全執行或者完全不執行。
BEGIN TRANSACTION
...
COMMIT TRANSACTION
ROLLBACK
-- SQL的ROLLBACK命令用來回退(撤銷)SQL語句
-- 執行DELETE操作,然后用ROLLBACK語句撤銷。
DELETE FROM Orders;
ROLLBACK;
COMMIT
一般的SQL語句都是針對數據庫表直接執行和編寫的。這就是所謂的隱式提交(implicit commit),即提交(寫或保存)操作是自動進行的。
在事務處理塊中,提交不會隱式進行。
-- SQL Server
-- 在這個SQL Server例子中,從系統中完全刪除訂單12345。
-- 因為涉及更新兩個數據庫表Orders和OrderItems,
-- 所以使用事務處理塊來保證訂單 不被部分刪除。
-- 最后的COMMIT語句僅在不出錯時寫出更改。
-- 如果第一條DELETE起作用,
-- 但第二條失敗,則DELETE不會提交。
BEGIN TRANSACTION
DELETE OrderItems WHERE order_num = 12345
DELETE Orders WHERE order_num = 12345
COMMIT TRANSACTION
使用保留點
使用簡單的ROLLBACK和COMMIT語句,就可以寫入或撤銷整個事務。但是,只對簡單的事務才能這樣做,復雜的事務可能需要部分提交或回退。
在MariaDB、MySQL和Oracle中創建占位符,可使用SAVEPOINT語句:
-- 創建保留點
SAVEPOINT delete1;
-- SQL Server 創建保留點
-- SAVE TRANSACTION delete1;
-- SQL Server例子
BEGIN TRANSACTION
INSERT INTO Customers(cust_id, cust_name)
VALUES('1000000010', 'Toys Emporium');
SAVE TRANSACTION StartOrder; -- 創建保留點
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20100,'2001/12/1','1000000010');
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 1, 'BR01', 100, 5.49);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 2, 'BR03', 100, 10.99);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION
21. 使用游標
什么是游標,如何使用游標
- 游標
結果集(result set)
SQL查詢所檢索出的結果。
有時,需要在檢索出來的行中前進或后退一行或多行,這就是游標的用途所在。游標(cursor)是一個存儲在DBMS服務器上的數據庫查詢,
它不是一條SELECT語句,而是被該語句檢索出來的結果集。在存儲了游標之后,應用程序可以根據需要滾動或瀏覽其中的數據。
說明:具體DBMS的支持
Microsoft Access不支持游標,所以本課的內容不適用于Microsoft Access。
MySQL 5已經支持存儲過程。因此,本課的內容不適用MySQL較早的版本。
SQLite支持的游標稱為步驟(step),下面講述的基本概念適用于SQLite的步驟,但語法可能完全不同。
不同的DBMS支持不同的游標選項和特性。常見的一些選項和特性如下。
- 能夠標記游標為只讀,使數據能讀取,但不能更新和刪除。
- 能控制可以執行的定向操作(向前、向后、第一、最后、絕對位置、相對位置等)。
- 能標記某些列為可編輯的,某些列為不可編輯的。
- 規定范圍,使游標對創建它的特定請求(如存儲過程)或對所有請求可訪問。
- 指示DBMS對檢索出的數據(而不是指出表中活動數據)進行復制,使數據在游標打開和訪問期間不變化。
2. 使用游標
使用游標涉及幾個明確的步驟:
- 在使用游標前,必須聲明(定義)它。這個過程實際上沒有檢索數據,它只是定義要使用的SELECT語句和游標選項。
- 一旦聲明,就必須打開游標以供使用。這個過程用前面定義的SELECT語句把數據實際檢索出來。
- 對于填有數據的游標,根據需要取出(檢索)各行。
- 在結束游標使用時,必須關閉游標,可能的話,釋放游標(有賴于具體的DBMS)。
使用DECLARE語句創建游標,這條語句在不同的DBMS中有所不同。DECLARE命名游標,并定義相應的SELECT語句,根據需要帶WHERE和其他子 句。
-- DECLARE語句用來定義和命名游標,
-- 這里為CustCursor。
-- SELECT語句定義一個包含沒有電子郵件地址(NULL值)的所有顧客的游標。
DECLARE CustCursor CURSOR
FOR
SELECT * FROM Customers
WHERE cust_email IS NULL
-- 打開游標
OPEN CURSOR CustCursor
現在可以用FETCH語句訪問游標數據了。FETCH指出要檢索哪些行,從何處檢索它們以及將它們放于何處(如變量名)。
DECLARE TYPE CustCursor IS REF CURSOR
RETURN Customers%ROWTYPE;
DECLARE CustRecord Customers%ROWTYPE
BEGIN
OPEN CustCursor;
FETCH CustCursor INTO CustRecord;
CLOSE CustCursor;
END;
22. 高級SQL特性
約束、索引和觸發器。
約束(constraint)
管理如何插入或處理數據庫數據的規則。
1. 主鍵
主鍵是一種特殊的約束,用來保證一列(或一組列)中的值是唯一的,而且永不改動。換句話說,表中的一列(或 多個列)的值唯一標識表中的每一行。這方便了直接或交互地處理表中的行。沒有主鍵,要安全地UPDATE或DELETE特定行而不影響其他行會非 常困難。
表中任意列只要滿足以下條件,都可以用于主鍵:
- 任意兩行的主鍵值都不相同。
- 每行都具有一個主鍵值(即列中不允許NULL值)。
- 包含主鍵值的列從不修改或更新。(大多數DBMS不允許這么做,但如果你使用的DBMS允許這樣做,好吧,千萬別!)
- 主鍵值不能重用。如果從表中刪除某一行,其主鍵值不分配給新行。
CREATE TABLE Vendors
(
vend_id CHAR(10) NOT NULL PRIMARY KEY, -- 主鍵
vend_name CHAR(50) NOT NULL,
vend_address CHAR(50) NULL,
vend_city CHAR(50) NULL,
vend_state CHAR(5) NULL,
vend_zip CHAR(10) NULL,
vend_country CHAR(50) NULL
);
給表的vend_id列定義添加關鍵字PRIMARY KEY,使其成為主鍵。
ALTER TABLE Vendors
ADD CONSTRAINT PRIMARY KEY (vend_id);
說明:SQLite中的鍵
SQLite不允許使用ALTER TABLE定義鍵,要求在初始的CREATE TABLE語句中定義它們。
外鍵
外鍵是表中的一列,其值必須列在另一表的主鍵中。外鍵是保證引用完整性的極其重要部分。
CREATE TABLE Orders
(
order_num INTEGER NOT NULL PRIMARY KEY, -- 主鍵
order_date DATETIME NOT NULL,
cust_id CHAR(10) NOT NULL REFERENCES Customers(cust_id) -- 外鍵
);
--ALTER TABLE語句中用CONSTRAINT語法來定義外鍵
ALTER TABLE Orders
ADD CONSTRAINT
FOREIGN KEY (cust_id) REFERENCES Customers (cust_id)
唯一約束
唯一約束用來保證一列(或一組列)中的數據是唯一的。它們類似于主鍵,但存在以下重要區別。
- 表可包含多個唯一約束,但每個表只允許一個主鍵。
- 唯一約束列可包含NULL值。
- 唯一約束列可修改或更新。
- 唯一約束列的值可重復使用。
- 與主鍵不一樣,唯一約束不能用來定義外鍵。
檢查約束
檢查約束用來保證一列(或一組列)中的數據滿足一組指定的條件。檢查約束的常見用途有以下幾點。
- 檢查最小或最大值。例如,防止0個物品的訂單(即使0是合法的數)。
- 指定范圍。例如,保證發貨日期大于等于今天的日期,但不超過今天起一年后的日期。
- 只允許特定的值。例如,在性別字段中只允許M或F。
CREATE TABLE OrderItems
(
order_num INTEGER NOT NULL,
order_item INTEGER NOT NULL,
prod_id CHAR(10) NOT NULL,
quantity INTEGER NOT NULL CHECK (quantity > 0), -- 添加檢查約束
item_price MONEY NOT NULL,
);
-- 檢查名為gender的列只包含M或F,可編寫如下的ALTER TABLE語句:
ADD CONSTRAINT CHECK (gender LIKE '[MF]')
2. 索引
索引用來排序數據以加快搜索和排序操作的速度。
數據庫索引的作用也一樣。主鍵數據總是排序的,這是DBMS的工作。因此,按主鍵檢索特定行總是一種快速有效的操作。
解決方法是使用索引。可以在一個或多個列上定義索引,使DBMS保存其內容的一個排過序的列表。在定義了索引后,DBMS以使用書的索引 類似的方法使用它。DBMS搜索排過序的索引,找出匹配的位置,然后檢索這些行
在開始創建索引前,應該記住以下內容:
- 索引改善檢索操作的性能,但降低了數據插入、修改和刪除的性能。在執行這些操作時,DBMS必須動態地更新索引。
- 索引數據可能要占用大量的存儲空間。
- 并非所有數據都適合做索引。取值不多的數據(如州)不如具有更多可能值的數據(如姓或名),能通過索引得到那么多的好處。
- 索引用于數據過濾和數據排序。如果你經常以某種特定的順序排序數據,則該數據可能適合做索引。
- 可以在索引中定義多個列(例如,州加上城市)。這樣的索引僅在以州加城市的順序排序時有用。如果想按城市排序,則這種索引沒有用處。
索引用CREATE INDEX語句創建(不同DBMS創建索引的語句變化很大)
--索引必須唯一命名。這里的索引名prod_name_ind在關鍵字CREATE INDEX之后定義。
-- ON用來指定被索引的表,而索引中包含的列(此例中僅有一 列)在表名后的圓括號中給出。
CREATE INDEX prod_name_ind
ON PRODUCTS (prod_name);
3. 觸發器
觸發器是特殊的存儲過程,它在特定的數據庫活動發生時自動執行。觸發器可以與特定表上的INSERT、UPDATE和DELETE操作(或組合)相關聯。
觸發器內的代碼具有以下數據的訪問權:
- INSERT操作中的所有新數據;
- UPDATE操作中的所有新數據和舊數據;
- DELETE操作中刪除的數據。
-- SQL Server
-- 對所有INSERT和UPDATE操作,將Customers表中的cust_state列轉換為大寫
CREATE TRIGGER customer_state
ON Customers
FOR INSERT, UPDATE
AS
UPDATE Customers
SET cust_state = Upper(cust_state)
WHERE Customers.cust_id inserted.cust_id;
提示:約束比觸發器更快
一般來說,約束的處理比觸發器快,因此在可能的時候,應該盡量使用約束。
-----------------------------------------大部分內容來自《sql必知必會》