[讀書筆記] SQL必知必會

SQL必知必會

檢索相關注意點

檢索去重數據關鍵字——DISTINCT

DISTINCT關鍵字指示數據庫只返回不同值,若使用DISTINCT關鍵字,必須放在列名前面。DISTINCT關鍵字作用於所有列。

SELECT DISTINCT * FROM TABLE;

檢索從第N行開始檢索前M行數據

SQL SERVER:

SELECT TOP 5 column FROM table;

ORACLE:

SELECT cloumn FROM table WHERE ROWNUM <= 5;

MySQL,MariaDB,PostgreSQL或者SQLite:

SELECT prod_name
FROM Products
LIMIT 5 OFFSET 5;

LIMIT 5指示MySQL等DBMS返回不超過5行的數據,OFFSET 5指示MySQL等DBMS返回從第5行起的5行數據。

數據庫檢索從第0行開始,因此LIMIT 1 OFFSET 1會檢索第2行。

三種註釋方式

-- 此後一行爲註釋
# 此後一行爲註釋
/* 此後
多行爲註釋,
直至 */

排序相關注意點

關係數據庫設計理論認爲,如果不明確規定排序順序,則不應該假定檢索出的數據的順序有任何意義。
爲了明確地爲數據進行排序,可以使用ORDER BY子句。ORDER BY子句取一個或多個列的名字。

SELECT cloumn_1 FROM table ORDER BY cloumn_1;

ORDER BY 需要保證爲SELECT語句的最後一條子句,即放在最後。
通常,ORDER BY子句使用的列可以爲顯示選擇的列,也可以是非檢索的列數據。
ORDER BY 後可以跟多個列,排序順序爲第一個列,當第一個列中有多行相同時,採用第二列進行排序,以此類推。

SELECT * FROM table ORDER BY column_1,column_2;

ORDER BY可以按相對列的位置進行排序。

SELECT column_1,column_2,column_3 FROM table ORDER BY 2,3;

這樣表示好處在於不用重新輸入列名,但缺點也很明顯:1.不明確給出列名有可能造成錯用列名排序;2.若修改索引列的數量和順序,則ORDER BY也需要更改,若遺漏將可能導致數據順序出現異常;3.若進行排序的列不再索引列中,無法使用此種寫法。
可以混合使用列名排序和位置排序。

排序方向

默認排序順序爲升序(從A到Z),還可以使用降序排列,指定DESC關鍵字。

SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC;

DESC關鍵字只應用到直接位於其前面的列名,如果想在多個列上進行降序排序,必須對每一列指定DESC關鍵字。
默認按字典序排序,若需要改變,需要DBMS Mananger進行修改。

過濾數據

只檢索滿足特定條件的數據,需要指定搜索條件,搜索條件也稱過濾條件,在SELECT語句中們加入WHERE關鍵字加搜索條件進行過濾。

SELECT prod_name, prod_price
FROM Products
WHERE prod_price = 3.49;

WHERE子句操作符

操作符 說明
= 等於
<> 不等於
!= 不等於
< 小於
<= 小於等於
!< 不小於
> 大於
>= 大於等於
!> 不大於
BETWEEN 在指定的兩個值之間
IS NULL 爲NULL值

單引號用來限定字符串。如果將值與字符串類型
的列進行比較,就需要限定引號。用來與數值列進行比較的值不用引號。
在使用BETWEEN時,必須指定兩個值——所需範圍的低端值和高端值。這兩個值必須用AND關鍵字分隔。BETWEEN匹配範圍中所有的值,包括指定的開始值和結束值。
SELECT語句有一個特殊的WHERE子句,可用來檢查具有NULL值的列。這個WHERE子句就是IS
NULL子句。

組合WHERE子句

AND、OR邏輯操作符

SQL允許給出多個WHERE子句。這些子句有兩種使用方式,即以AND子句或OR子句等邏輯操作符的方式使用。

SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;

AND指示DBMS只返回滿足所有給定條件的行。
可以增加多個過濾條件,每個條件間都要使用AND關鍵字。

SELECT prod_name, prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = ‘BRS01’;

OR操作符指示DBMS檢索匹配任一條件的行

SELECT prod_name, prod_price
FROM Products WHERE vend_id = 'DLL01' OR vend_id = ‘BRS01’
AND prod_price >= 10;

AND在求值過程中優先級比OR更高,使用圓括號進行分組可以避免查詢條件混亂,消除歧義。

SELECT prod_name, prod_price
FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = ‘BRS01’)
AND prod_price >= 10;

IN操作符

IN操作符用來指定條件範圍,範圍中的每個條件都可以進行匹配。IN取一組由逗號分隔、括在圓括號中的合法值。
優點:

  • 在有很多合法選項時(一般超過三個),IN操作符的語法更清楚,更直觀。
  • 在與其他AND和OR操作符組合使用IN時,求值順序更容易管理。
  • IN操作符一般比一組OR操作符執行得更快。
  • IN的最大優點是可以包含其他SELECT語句,能夠更動態地建立WHERE子句。

NOT操作符

WHERE子句中的NOT操作符有且只有一個功能,那就是否定其後所跟的任何條件。NOT關鍵字可以用在要過濾的列前,而不僅是在其後。

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;

在更復雜的子句中,NOT是非常有用的。例如,在與IN操作符聯合使用時,NOT可以非常簡單地找出與條件列表不匹配的行。

用通配符進行過濾

前面的過濾條件都是使用已知的值。利用通配符,可以創建比較特定數據的搜索模式。

通配符(wildcard)
用來匹配值的一部分的特殊字符。
搜索模式(search pattern)
由字面值、通配符或兩者組合構成的搜索條件。

爲在搜索子句中使用通配符,必須使用LIKE操作符。LIKE指示DBMS,後跟的搜索模式利用通配符匹配而不是簡單的相等匹配進行比較。
通配符搜索只能用於文本字段(串),非文本數據類型字段不能使用通配符搜索。

百分號通配符(%)

最常用的通配符是百分號(%)。在搜索串中,%表示任何字符出現任意次數。

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';

%告訴DBMS接受Fish之後的任意字符,不管它有多少字符。
通配符可在搜索模式中的任意位置使用,並且可以使用多個通配符。

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '%bean bag%';

搜索模式’%bean bag%'表示匹配任何位置上包含文本bean bag的值,不論它之前或之後出現什麼字符。

需要特別注意,%代表搜索模式中給定位置的0個、1個或多個字符。通配符%看起來像是可以匹配任何東西,但有個例外,這就是NULL。子句WHERE prod_name LIKE '%'不會匹配產品名稱爲NULL的行。

下劃線(_)通配符

下劃線的用途與%一樣,但它只匹配單個字符,而不是多個字符。

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear';

與%能匹配0個字符不同,_總是剛好匹配一個字符,不能多也不能少。

方括號([ ])通配符

方括號([])通配符用來指定一個字符集,它必須匹配指定位置(通配符的位置)的一個字符。

-- 找出所有名字以J或M起頭的聯繫人
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;

[JM]匹配任何以方括號中字母開頭的聯繫人名,它也只能匹配單個字符。因此,任何多於一個字符的名字都不匹配。[JM]之後的%通配符匹配第一個字符之後的任意數目的字符,返回所需結果。此通配符可以用前綴字符^(脫字號)來否定。

-- 查詢匹配不以J或M起頭的任意聯繫人名
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[^JM]%'
ORDER BY cust_contact;

當然,也可以使用NOT操作符得出相同的結果。^的唯一優點是在使用多個WHERE子句時可以簡化語法。

  • 不要過度使用通配符。如果其他操作符能達到相同的目的,應該使用其他操作符。
  • 在確實需要使用通配符時,也儘量不要把它們用在搜索模式的開始處。把通配符置於開始處,搜索起來是最慢的。
  • 仔細注意通配符的位置。如果放錯地方,可能不會返回想要的數據。

計算字段

計算字段並不實際存在於數據庫表中。計算字段是運行時在SELECT語句內創建的。
需要特別注意,只有數據庫知道SELECT語句中哪些列是實際的表列,哪些列是計算字段。從客戶端(如應用程序)來看,計算字段的數據與其
他列的數據的返回方式相同。

拼接字段

Access和SQL Server使用+號。DB2、Oracle、PostgreSQL、SQLite和Open Office Base使用||。在MySQL和MariaDB中,必須使用特殊的函數。

SELECT vend_name + ' (' + vend_country + ')'
FROM Vendors
ORDER BY vend_name;

“||”

SELECT vend_name || ' (' || vend_country || ')'
FROM Vendors
ORDER BY vend_name;

爲正確返回格式化的數據,必須去掉空格。這可以使用SQL的RTRIM()函數來完成。

SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
FROM Vendors
ORDER BY vend_name;

MySQL ↓

SELECT Concat(vend_name, ' (', vend_country, ')')
AS vend_title
FROM Vendors
ORDER BY vend_name;

SELECT語句本身與以前使用的相同,只不過這裏的計算字段之後跟了文本AS vend_title。它指示SQL創建一個包含指定計算結果的名爲vend_title的計算字段。任何客戶端應用都可以按名稱引用這個列,就像它是一個實際的表列一樣。

說明:AS通常可選
在很多DBMS中,AS關鍵字是可選的,不過最好使用它,這被視爲一條最佳實踐。
提示:別名的其他用途
別名還有其他用途。常見的用途包括在實際的表列名包含不合法的字符(如空格)時重新命名它,在原來的名字含混或容易誤解時擴充它。
警告:別名
別名既可以是一個單詞也可以是一個字符串。如果是後者,字符串應該括在引號中。雖然這種做法是合法的,但不建議這麼去做。多單詞的名字可讀性高,不過會給客戶端應用帶來各種問題。因此,別名最常見的使用是將多個單詞的列名重命名爲一個單詞的名字。

算術運算

計算字段的另一常見用途是對檢索出的數據進行算術計算。

SELECT prod_id,
quantity,
item_price,
quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;
操作符 說明
+
-
*
/

SELECT語句爲測試、檢驗函數和計算提供了很好的方法。雖然SELECT通常用於從表中檢索數據,但是省略了FROM子句後就是簡單地訪問和處理表達式,例如SELECT 3 * 2;將返回6,SELECT Trim(’ abc ');將返回abc,SELECT Now();使用Now()函數返回當前日期和時間。

函數名 語法
提取字符串的組成部分 MYSQL:SUBSTRING()
數據類型轉換 MYSQL: CONVERT()
取當前日期 MYSQL:CURDATE()

使用函數

大多數SQL支持以下類型函數:

  • 用於處理文本字符串(如刪除或填充值,轉換值爲大寫或小寫)的文本函數。
  • 用於在數值數據上進行算術操作(如返回絕對值,進行代數運算)的數值函數。
  • 用於處理日期和時間值並從這些值中提取特定成分(如返回兩個日期之差,檢查日期有效性)的日期和時間函數。
  • 返回DBMS正使用的特殊信息(如返回用戶登錄信息)的系統函數。

函數可以作爲SELECT語句的WHERE子句中使用,在其他SQL語句中使用等。

文本處理函數

函數 說明
LEFT() 返回字符串左邊的字符
LENGTH()(也使用DATALENGTH()或者LEN()) 返回字符串的長度
LOWER() 將字符串轉換爲小寫
LTRIM 去除字符串左邊的空格
RIGHT()(或使用子字符串函數) 返回字符串右邊的字符
RTRIM 去掉字符串右邊的空格
SOUNDEX() 返回字符串的SOUNDEX值
UPPER() 將字符串轉爲大寫

SOUNDEX是一個將任何文本串轉換爲描述其語音表示的字母數字模式的算法。SOUNDEX考慮了類似的發音字符和音節,使得能對字符串進行發音比較而不是字母比較。雖然SOUNDEX不是SQL概念,但多數DBMS都提供對SOUNDEX的支持。

SELECT cust_name, cust_contact
FROM Customers
WHERE SOUNDEX(cust_contact) = SOUNDEX('Michael Green');

MySQL可以使用YEAR()的函數中提取年份。

數值處理函數

數值處理函數:

函數 說明
ABS() 返回一個數的絕對值
COS() 返回一個角度的餘弦
EXP() 返回一個數的指數值
PI() 返回圓周率
SIN() 返回一個角度的正弦
SQRT() 返回一個數的平方根
TAN() 返回一個角度的正切

聚集函數

  • 確定表中行數(或者滿足某個條件或包含某個特定值的行數);
  • 獲得表中某些行的和;
  • 找出表列(或所有行或某些特定的行)的最大值、最小值、平均值。
函數 說明
AVG() 返回某列的平均值
COUNT() 返回某列的行數
MAX() 返回某列的最大值
MIN() 返回某列的最小值
SUM() 返回某列值之和
SELECT AVG(prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';

VG()只能用來確定特定數值列的平均值,而且列名必須作爲函數參數給出。爲了獲得多個列的平均值,必須使用多個AVG()函數。
AVG()函數忽略列值爲NULL的行。

COUNT()
利用COUNT()函數確定表中行數或符合特定條件的行數。

  • 使用COUNT(*)對錶中行的數目進行技術,不管表列中包含的是空值(NULL)還是非空值
  • 使用COUNT(column)對特定列中具有值的行進行計數,忽略NULL值。

MAX()
MAX()返回指定列中的最大值。

雖然MAX()一般用來找出最大的數值或日期值,但許多(並非所有)DBMS允許將它用來返回任意列中的最大值,包括返回文本列中的最大值。在用於文本數據時,MAX()返回按該列排序後的最後一行。MAX()函數忽略列值爲NULL的行。

MIN()
和MAX()使用方法相反。

SUM()
SUM()用來返回指定列值得和,也可以用來合計計算值。

SELECT SUM(item_price*quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;

利用標準的算術操作符,所有聚集函數都可用來執行多個列上的計算。SUM()函數忽略列值爲NULL的行。

聚集不同值

以上五個聚集函數都可以如下使用:

  • 對所有行執行計算,指定ALL參數或不指定參數(因爲ALL是默認行爲)。
  • 只包含不同的值,指定DISTINCT參數。
  • ALL參數不需要指定,因爲它是默認行爲。如果不指定DISTINCT,則假定爲ALL。
  • DISTINCT不能用於COUNT()
    如果指定列名,則DISTINCT只能用於COUNT()。DISTINCT不能用於COUNT(
    )。類似地,DISTINCT必須使用列名,不能用於計算或表達式。
  • 將DISTINCT用於MIN()和MAX()
    雖然DISTINCT從技術上可用於MIN()和MAX(),但這樣做實際上沒有價值。一個列中的最小值和最大值不管是否只考慮不同值,結果都是相同的。
  • 除了這裏介紹的DISTINCT和ALL參數,支持對查詢結果的子集進行計算的TOP和TOP PERCENT。

組合聚集函數

SELECT語句可根據需要包含多個聚集函數。

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;

在指定別名以包含某個聚集函數的結果時,不應該使用表中實際的列名。

數據分組

使用分組可以將數據分爲多個邏輯組,對每個組進行聚集計算。分組是使用SELECT語句的GROUP BY子句建立的。

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;

GROUP BY子句指示DBMS按vend_id排序並分組數據。這就會對每個vend_id而不是整個表計算num_prods一次。

  • GROUP BY子句可以包含任意數目的列,因而可以對分組進行嵌套,更細緻地進行數據分組。
  • 如果在GROUP BY子句中嵌套了分組,數據將在最後指定的分組上進行彙總。換句話說,在建立分組時,指定的所有列都一起計算(所以不能從個別的列取回數據)。
  • GROUP BY子句中列出的每一列都必須是檢索列或有效的表達式(但不能是聚集函數)。如果在SELECT中使用表達式,則必須在GROUP BY子句中指定相同的表達式。不能使用別名。
  • 大多數SQL實現不允許GROUP BY列帶有長度可變的數據類型(如文本或備註型字段)
  • 除聚集計算語句外,SELECT語句中的每一列都必須在GROUP BY子句中給出
  • 如果分組列中包含具有NULL值的行,則NULL將作爲一個分組返回。如果列中有多行NULL值,它們將分爲一組。
  • GROUP BY子句必須出現在WHERE子句之後,ORDER BY子句之前。

過濾分組

HERE不能完成任務,因爲WHERE過濾指定的是行而不是分組。事實上,WHERE沒有分組的概念。

SQL爲此提供了另一個子句,就是HAVING子句。HAVING非常類似於WHERE。事實上,目前爲止所學過的所有類型的WHERE子句都可以用HAVING來替代。唯一的差別是,WHERE過濾行,而HAVING過濾分組。WHERE和HAVING可以一起使用。

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;

分組和排序

ORDER BY GROUP BY
對產生的輸出排序 對行分組,但輸出可能不是分組的順序
任意列都可以使用(甚至非選擇的列也可以使用) 只可能使用選擇列或表達式列,而且必須使用每個選擇列表達式
不一定需要 如果與聚集函數一起使用列(或表達式),則必須使用

一般在使用GROUP BY子句時,應該也給出ORDER BY子句。這是保證數據正確排序的唯一方法。千萬不要僅依賴GROUP BY排序數據。

SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3;
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items, order_num;

SELECT子句順序
| 子句 | 說明 | 是否必須使用 |

SELECT 要返回的列或表達式
FROM 從中檢索數據的表
WHERE 行級過濾
GROUP BY 分組說明
組級過濾
ORDER BY 輸出排序順序

子查詢

SQL還允許創建子查詢(subquery),即嵌套在其他查詢中的查詢。
假如需要列出訂購物品RGAN01的所有顧客:

  1. 檢索包含物品RGAN01的所有訂單的編號。
  2. 檢索具有前一步驟列出的訂單編號的所有顧客的ID。
  3. 檢索前一步驟返回的所有顧客ID的顧客信息。
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';

order_num
-----------
20007
20008

SELECT cust_id
FROM Orders
WHERE order_num IN (20007,20008);

cust_id
----------
1000000004
1000000005


SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');

子查詢總是從內向外處理。包含子查詢的SELECT語句難以閱讀和調試,它們在較爲複雜時更是如此。把子查詢分解爲多行並進行適當的縮進,能極大地簡化子查詢的使用。

在WHERE子句中使用子查詢能夠編寫出功能很強且很靈活的SQL語句。對於能嵌套的子查詢的數目沒有限制,不過在實際使用時由於性能的限制,不能嵌套太多的子查詢。

警告:只能是單列
作爲子查詢的SELECT語句只能查詢單個列。企圖檢索多個列將返回錯誤。

作爲計算阻斷使用字符串

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語句中操作多個表,就應使用完全限定列名來避免歧義。

聯結

聯結是一種機制,用來在一條SELECT語句中關聯表,因此稱爲聯結。要理解聯結不是物理實體。換句話說,它在實際的數據庫表中並不存在。DBMS會根據需要建立聯結,它在查詢執行期間一直存在。

SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;

WHERE子句指示DBMS將Vendors表中的vend_id與Products表中的vend_id匹配起來。要匹配的兩列指定爲Vendors.vend_id和Products.vend_id。這裏需要這種完全限定列名,如果只給出vend_id,DBMS就不知道指的是哪一個(每個表中有一個)。

笛卡兒積(cartesian product)
由沒有聯結條件的表關係返回的結果爲笛卡兒積。檢索出的行的數目將是第一個表中的行數乘以第二個表中的行數。

不要忘了WHERE子句
要保證所有聯結都有WHERE子句,否則DBMS將返回比想要的數據多得多的數據。同理,要保證WHERE子句的正確性。不正確的過濾條件會導
致DBMS返回不正確的數據。

使用的聯結稱爲等值聯結(equijoin),它基於兩個表之間的相等測試。這種聯結也稱爲內聯結(inner join)。其實,可以對這種聯結使用稍微不同的語法,明確指定聯結的類型。

SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.Vend_id;

聯結條件用特定的ON子句而不是WHERE子句給出。傳遞給ON的實際條件與傳遞給WHERE的相同。

SQL不限制一條SELECT語句中可以聯結的表的數目。創建聯結的基本規則也相同。首先列出所有表,然後定義表之間的關係。

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在運行時關聯指定的每個表,以處理聯結。這種處理可能非常耗費資源,因此應該注意,不要聯結不必要的表。聯結的表越多,性能下降越厲害。

聯結中表的最大數目
雖然SQL本身不限制每個聯結約束中表的數目,但實際上許多DBMS都有限制。

使用表別名

SQL除了可以對列名和計算字段使用別名,還允許給表名起別名。這樣做有兩個主要理由:

  • 縮短SQL語句;
  • 允許在一條SELECT語句中多次使用相同的表。
SELECT cust_name, cust_contact
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';

警告:Oracle中沒有AS
Oracle不支持AS關鍵字。要在Oracle中使用別名,可以不用AS,簡單地指定列名即可(因此,應該是Customers C,而不是Customers ASC)。需要注意,表別名只在查詢執行中使用。與列別名不一樣,表別名不返回到客戶端。

不同類型的聯結

聯結類型:

  • 內聯結或等值聯結
  • 自聯結(self-join)
  • 自然聯結(natural join)
  • 外聯結(outer join)
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
AND c2.cust_contact = 'Jim Jones';

提示:用自聯結而不用子查詢
自聯結通常作爲外部語句,用來替代從相同表中檢索數據的使用子查詢語句。雖然最終的結果是相同的,但許多DBMS處理聯結遠比處理子
查詢快得多。應該試一下兩種方法,以確定哪一種的性能更好。

自然聯結
自然聯結要求你只能選擇那些唯一的列,一般通過對一個表使用通配符(SELECT *),而對其他表的列使用明確的子集來完成。

SELECT C.*, O.order_num, O.order_date,
	OI.prod_id, OI.quantity, OI.item_price
FROM Customs AS C, OrderItems AS OI
WHERE C.cust_id = O.cut_id 
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';

外聯結
許多聯結將一個表中的行與另一個表中的行相關聯,但有時候需要包含沒有關聯行的那些行。例如,可能需要使用聯結完成以下工作:

  • 對每個顧客下的訂單進行計數,包括那些至今尚未下訂單的顧客;
  • 列出所有產品以及訂購數量,包括沒有人訂購的產品;
  • 計算平均銷售規模,包括那些至今尚未下訂單的顧客。

內聯結:檢索所有顧客及其訂單

SELECT Customers.cust_id, Order.order_num
FROM Customers INNER JOIN Orders On Customers.cust_id = Orders.cust_id;

外聯結:檢索包括沒有訂單顧客在內的所有顧客

SELECT Customers.cust_id, Orders.order_num
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;

外聯結還包括沒有關聯行的行。在使用OUTER JOIN語法時,必須使用RIGHT或LEFT關鍵字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右邊的表,而LEFT指出的是OUTER JOIN左邊的表。

還存在另一種外聯結,就是全外聯結(full outer join),它檢索兩個表中的所有行並關聯那些可以關聯的行。與左外聯結或右外聯結包含一個表的不關聯的行不同,全外聯結包含兩個表的不關聯的行。全外聯結的語法如下:

SELECT Customers.cust_id, Orders.order_num
FROM Orders FULL OUTER JOIN Customers
ON Orders.cust_id = Customers.cust_id;

Mysql不支持FULL OUTER JOIN

使用帶聚集函數的聯結

所有顧客及每個顧客所下的訂單數

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;

使用左外部聯結來包含所有顧客,甚至包含那些沒有任何訂單的顧客。

SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;

組合查詢

SQL允許執行多個查詢(多條SELECT語句),並將結果作爲一個查詢結果集返回。這些組合查詢通常稱爲並(union)或複合查詢(compound query)。
主要有兩種情況需要使用組合查詢:

  • 在一個查詢中從不同的表返回結構數據;
  • 對一個表執行多個查詢,按一個查詢返回數據。

提示:組合查詢和多個WHERE條件
多數情況下,組合相同表的兩個查詢所完成的工作與具有多個WHERE子句條件的一個查詢所完成的工作相同。換句話說,任何具有多個WHERE子句的SELECT語句都可以作爲一個組合查詢。

UNION關鍵字

利用UNION,可給出多條SELECT語句,將它們的結果組合成一個結果集。
部分情況下,使用UNION可能比使用WHERE子句更爲複雜。但對於較複雜的過濾條件,或者從多個表(而不是一個表)中檢索數據的情形,使用UNION可能會使處理更簡單。
UNION中的每個查詢必須包含相同的列、表達式或聚集函數(不過,各個列不需要以相同的次序列出)。
列數據類型必須兼容:類型不必完全相同,但必須是DBMS可以隱含轉換的類型(例如,不同的數值類型或不同的日期類型)。
使用UNION時,重複的行會被自動取消。如果想返回所有的匹配行,可使用UNION ALL而不是UNION。
UNION幾乎總是完成與多個WHERE條件相同的工作。UNION ALL爲UNION的一種形式,它完成WHERE子句完成不了的工作。如果確實需要每個條件的匹配行全部出現(包括重複行),就必須使用UNION ALL,而不是WHERE。
在用UNION組合查詢時,只能使用一條ORDER BY子句,它必須位於最後一條SELECT語句之後。對於結果集,不存在用一種方式排序一部分,而又用另一種方式排序另一部分的情況,因此不允許使用多條ORDER BY子句。
UNION在需要組合多個表的數據時也很有用,即使是有不匹配列名的表,在這種情況下,可以將UNION與別名組合,檢索一個結果集。

數據插入

  • 插入完整的行;
  • 插入行的一部分;
  • 插入某些查詢的結果。

事物處理

術語:

  • **事務(transaction)**指一組SQL語句;
  • **回退(rollback)**指撤銷指定SQL語句的過程;
  • **提交(commit)**指將未存儲的SQL語句結果寫入數據庫表;
  • **保留點(savepoint)**指事務處理中設置的臨時佔位符(placeholder),可以對它發佈回退(與回退整個事務處理不同)。

**注:**事務處理用來管理INSERT、UPDATE和DELETE語句。不能回退SELECT語句(回退SELECT語句也沒有必要),也不能回退CREATE或DROP操作。事務處理中可以使用這些語句,但進行回退時,這些操作也不撤銷。

使用簡單的ROLLBACK和COMMIT語句,就可以寫入或撤銷整個事務。但是,只對簡單的事務才能這樣做,複雜的事務可能需要部分提交或回退。
要支持回退部分事務,必須在事務處理塊中的合適位置放置佔位符。這樣,如果需要回退,可以回退到某個佔位符。這些佔位符稱爲保留點。

在MariaDB、MySQL和Oracle中創建佔位符,可使用SAVEPOINT語句:

mysql:
SAVEPOINT delete1;

ROLLBACK TO delete1;

每個保留點都要取能夠標識它的唯一名字,以便在回退時,DBMS知道回退到何處。

可以在SQL代碼中設置任意多的保留點,越多越好。爲什麼呢?因爲保留點越多,你就越能靈活地進行回退。

遊標

遊標(cursor)是一個存儲在DBMS服務器上的數據庫查詢,它不是一條SELECT語句,而是被該語句檢索出來的結果集。在存儲了遊標之後,應用程序可以根據需要滾動或瀏覽其中的數據。

  • 能夠標記遊標爲只讀,使數據能讀取,但不能更新和刪除。
  • 能控制可以執行的定向操作(向前、向後、第一、最後、絕對位置、相對位置等)。
    能標記某些列爲可編輯的,某些列爲不可編輯的。
  • 規定範圍,使遊標對創建它的特定請求(如存儲過程)或對所有請求可訪問。
  • 指示DBMS對檢索出的數據(而不是指出表中活動數據)進行復制,使數據在遊標打開和訪問期間不變化。
  • 遊標主要用於交互式應用,其中用戶需要滾動屏幕上的數據,並對數據進行瀏覽或做出更改。

索引

  • 索引改善檢索操作的性能,但降低了數據插入、修改和刪除的性能。在執行這些操作時,DBMS必須動態地更新索引。
  • 索引數據可能要佔用大量的存儲空間。
  • 並非所有數據都適合做索引。取值不多的數據(如州)不如具有更多可能值的數據(如姓或名),能通過索引得到那麼多的好處。
  • 索引用於數據過濾和數據排序。如果你經常以某種特定的順序排序數據,則該數據可能適合做索引。
  • 可以在索引中定義多個列(例如,州加上城市)。這樣的索引僅在以州加城市的順序排序時有用。如果想按城市排序,則這種索引沒有用處。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章