SQL 入門篇

前言

讀《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

  1. 數據插入
    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語句管理事務處理。

  1. 事務處理
    使用事務處理(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. 使用游標

什么是游標,如何使用游標

  1. 游標

結果集(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必知必會》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容