【長文乾貨】MySQL必知必會——增刪改查知識總結

1.準備工作

1.1使用wampserver安裝本地服務器

1.2安裝Navicat(交互式的mysql控制軟件):
Navicat for mysql破解

1.3下載本文所需數據庫文件
MySQL Crash Course

1.4登錄你的mysql

1.5將下載的sql文件導入到本地mysql服務器
https://blog.csdn.net/zoroday/article/details/54809042

2.上手使用Mysql

2.1 查看你的數據庫和表

查看所有可用的數據庫,返回一個數據庫列表

SHOW DATABASES;

選擇數據庫

USE crashcourse;

查看當前數據庫中所有可用的表

SHOW TABLES;

查看某個表中的所有列(相當於SPSS中的變量視圖)

SHOW COLUMNS FROM customers;

在這裏插入圖片描述

講解:Field是列名,Type是數據類型(括號中的是數據長度),Null是是否允許Null,Key是鍵信息(如主鍵),Default是默認值,Extra是其他信息(如自動增量)

2.2 其他可能會用到的SHOW語句

SHOW STATUS        --顯示服務器狀態信息
SHOW GRANTS        --顯示用戶的安全權限
SHOW ERRORS        --顯示服務器錯誤
SHOW WARNINGS      --顯示服務器警告信息

2.3 SQL基礎知識彙總

2.3.1 子句(clause) SQL語句由子句構成,有些子句是必須的有些是可選的,如SELECT語句的FROM子句是必須的,ORDER BY子句是可選的。
2.3.2 SQL中完全不區分大小寫,對錶名,列名也是。
2.3.3 操作符(operator),用來聯結或改變WHERE子句中的子句的關鍵字。也叫邏輯操作符(logical operator)
2.3.4 通配符(wildcard):用來匹配值的一部分的特殊字符
2.3.5 謂詞(predicate):從技術上說,LIKE其實是謂詞而不是操作符。
2.3.6 字段(field):基本上與列(column)意思相同,經常互換使用,不過數據庫列一般稱爲列,而術語字段常用在計算字段的連接上。
2.3.7 別名(alias):是一個字段或值的替代名
2.3.8 聚集函數(aggregate function):運行在行組上,計算和返回單個值的函數。
2.3.9 外鍵(foreign key):外鍵爲某個表中的一列,它包含另一個表的主鍵值,定義了兩個表間的關係。

3.檢索數據

3.1 基本按列檢索語句

SELECT prod_name FROM products;      --檢索一列
SELECT prod_id, prod_name, prod_price FROM products;      --檢索多列
SELECT * FROM products;      --檢索所有列

3.2 檢索唯一值(去除重複項)

SELECT DISTINCT vend_id FROM products;

注意: 如果DISTICT後面帶有多列,如SELECT DISTINCT vend_id, prod_price,DISTINCT不是隻應用於後面接的第一列,而是後面的所有列。如這個例子中,除非兩行的vend_id和prod_price都相同纔會去除重複項,vend_id相同而prod_price不同的兩行都會被檢索出來。

3.3 用LIMIT限制結果,僅返回第一行或前幾行

SELECT prod_name FROM products LIMIT 5;           --指示MySQL返回不多於5行
SELECT prod_name FROM products LIMIT 3, 5;        --返回從行3開始的5行
SELECT prod_name FROM products LIMIT 5 OFFSET 3;        --返回從行3開始的5行,等價於上面

注意:
①第一行爲行0而不是行1
②LIMIT中指定的行數爲最大數,如果沒有足夠的行,Mysql就會返回它能返回的那麼多行。

3.4 使用完全限定的表名

SELECT products.prod_name FROM crashcourse.products;

代碼中限定了行所屬的表名和表所屬的數據庫名,這個與3.1中的語句等價,後面會介紹限定表名的作用。

3.5 對檢索數據排序

SELECT prod_name FROM products ORDER BY prod_name;
SELECT prod_name FROM products ORDER BY prod_id;       --使用非檢索列用於排序是完全合法的

SELECT prod_id, prod_price, prod_name FROM products 
ORDER BY prod_price, prod_name;               --按多個列排序

SELECT prod_price, prod_name FROM products ORDER BY prod_price DESC;  --降序排列

SELECT prod_id, prod_price, prod_name FROM products 
ORDER BY prod_price DESC, prod_name;    --按多列排序時,DESC只作用於前面的列名

注意:如果想在多個列上使用降序排序,則需要爲每個列指定DESC關鍵字
與DESC相反的是ASC關鍵字,代表升序,它是默認選項。
在字典(dictionary)排序順序中,A和a是相同的順序,是Mysql的默認行爲,但是這個是可以被DBA設置的,如果需要改變這種排序,靠ORDER BY無法做到只能找DBA。

3.5.1 使用ORDER BY 和LIMIT結合找到一列中的最高值或最低值

SELECT prod_price FROM products ORDER BY prod_price DESC LIMIT 1;

LIMIT 必須位於ORDER BY之後

4. 過濾數據

4.1 基礎WHERE子句

SELECT prod_name, prod_price FROM products WHERE prod_price < 10;
SELECT prod_name, prod_price FROM products WHERE prod_name = 'fuses';
SELECT * FROM products WHERE prod_price BETWEEN 5 AND 10;     --BETWEEN包括起始值結束值
SELECT cust_id FROM customers WHERE cust_email IS NULL;
SELECT cust_id FROM customers WHERE cust_email IS NOT NULL;

WHERE子句操作符包括=, <>, !=, BETWEEN,<, <=等;
WHERE在ORDER BY之前
將值與字符串比較時,字符串要加單引號
過濾數據時,即使在選擇不具有特定值得行時(如!=),也不會返回具有NULL的值,因此在過濾數據時,一定要驗證返回數據中確實給出了被過濾列具有NULL值的行。

4.2 通過操作符組合WHERE子句

操作符(operator),用來聯結或改變WHERE子句中的子句的關鍵字。

4.2.1 AND和OR操作符

SELECT * FROM products WHERE vend_id = 1003 AND prod_price <= 10;
SELECT * FROM products WHERE vend_id = 1002 OR vend_id = 1003;

-- 當AND和OR同時存在時,AND優先級大於OR,但是推薦用()限定順序:
SELECT * FROM products WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >=10;

4.2.2 IN和NOT操作符

-- 選擇vend_id爲1002或1003的所有行:
SELECT * FROM products WHERE vend_id IN (1002, 1003) ORDER BY prod_name;
-- 選擇vend_id不爲1002或1003的所有行:
SELECT * FROM products WHERE vend_id NOT IN (1002, 1003) ORDER BY prod_name;

上面的代碼中,IN的作用等價於OR,但是更推薦用IN,因爲如果選項更多時IN更簡潔清晰,而且執行更快,同時IN還可以包含其他SELECT子句(後面介紹)
NOT只有一個功能:否定它後面所跟的任何條件
Mysql中支持NOT對IN, BETWEEN和EXISTS子句取反,而多數其他DBMS支持對各種條件取反。

4.3 構造通配符搜索模式進行過濾

通配符(wildcard):用來匹配值的一部分的特殊字符
搜索模式(search pattern):由字面值、通配符或兩者組合成的搜索條件
在搜索子句中使用通配符必須使用LIKE操作符,LIKE指示MySQL後跟的搜索模式利用通配符而不是直接相等匹配進行比較
謂詞(predicate):從技術上說,LIKE其實是謂詞而不是操作符。

  • %通配符表示任何字符出現任意次數,包括0次
  • _通配符表示任何的單個字符,不能多也不能少
SELECT * FROM products WHERE prod_name LIKE 'jet%';
SELECT * FROM products WHERE prod_name LIKE '%anvil%';
SELECT * FROM products WHERE prod_name LIKE 's%e';

SELECT * FROM products WHERE prod_name LIKE '_ ton anvil';

注意
①如果Mysql配置爲區分大小寫的話,結果將會不一樣
②注意尾部空格,如果被搜索項後面帶有一個或多個空格時,那麼將匹配不到,解決方法是在搜索模式後也加一個%,更好的辦法是通過函數去掉首尾空格
③%無法匹配NULL值
④通配符搜索比其他搜索處理時間更長,因此可以用其他代替儘量用其他代替,而且除非必要否則不要講通配符置於搜索模式的開始處,因爲這樣是最慢的。

4.4 用正則表達式進行搜索

正則表達式(Regular Expression)是用來匹配文本的特殊的串(字符集合)

4.4.1 REGEXP與LIKE的對比
正則表達式中使用REGEXP關鍵字,它告訴MySQL後面所跟的東西爲正則表達式。

REGEXP與LIKE的區別在於,LIKE會匹配整個列值,如果被匹配的文本在列值中出現,LIK將不會找到它,相應的行也不會返回(除非使用通配符),而REGEXP是在列值內匹配,如果被匹配的文本在列值中出現,REGEXP將會找到它,相應的行將會被返回。

SELECT prod_name FROM products WHERE prod_name LIKE '1000';
SELECT prod_name FROM products WHERE prod_name REGEXP '1000';;

如上方的代碼,LIKE語句不會返回任何行,REGEXP則會返回’JetPack 1000’
不過REGEXP也可以使用^和$定位符用來匹配整個列值。

4.4.2 幾個基本字符

. :匹配任意一個字符
| :正則表達式的OR操作符
[] :匹配幾個字符之一,另一種形式的OR操作符
- :結合[]匹配範圍

-- 返回JetPack 1000和JetPack 2000
SELECT prod_name FROM products WHERE prod_name REGEXP '.000';
SELECT prod_name FROM products WHERE prod_name REGEXP '1000|2000';

-- 匹配1 ton 或 2 ton 或 3ton
SELECT prod_name FROM products WHERE prod_name REGEXP '[123] ton'

-- 匹配1 ton 或 2 ton 或 3ton 或 4 ton 或 5 ton  等價於'[12345] ton'
SELECT prod_name FROM products WHERE prod_name REGEXP '[1-5] ton'

4.4.3 轉義(escaping)字符 \\

對於特殊字符. [] | -,如果需要匹配這些字符,就需要通過\\進行轉義,\\-表示查找-\\.表示查找.\\\轉義自身,表示查找\
其他可以轉義的元字符、
在這裏插入圖片描述
4.4.4 匹配字符類

字符類(character class):有時候需要檢索出我們需要的數字、所有字母字符或所有數字字母字符等的匹配,我們可以使用預定義的字符集,稱爲字符類;如下:
在這裏插入圖片描述
4.4.5 匹配多個實例

有時候需要對匹配的數目進行更強的控制,比如:尋找所有的數,不管數中包含多少數字,或尋找一個單詞並尾隨一個s(如果存在)等情況,我們可以利用正則表達式中的重複元字符來完成;如下
在這裏插入圖片描述
4.4.6 定位符

有時候爲了匹配特定位置的文本,需要使用定位符,常用定位符列表如下:
在這裏插入圖片描述
4.4.7 舉例

mysql> SELECT prod_name FROM products WHERE prod_name REGEXP '\\([0-9] sticks?\\)';

+----------------+
| prod_name      |
+----------------+
| TNT (1 stick)  |
| TNT (5 sticks) |
+----------------+

\\對括號進行轉義,[0-9]表示匹配任意數字,stick?匹配stick和sticks

mysql> SELECT prod_name FROM products WHERE prod_name REGEXP '[[:digit:]]{4}';
+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+

[:digit:]匹配任意數字,因而它爲一個數字的集合。{4}則確切地要求它前面的字符(任意數字)出現4次,所以上述表達式的意思是匹配連在一起的任意4位數字。

mysql> SELECT prod_name FROM products WHERE prod_name REGEXP '^[0-9\\.]';
+--------------+
| prod_name    |
+--------------+
| .5 ton anvil |
| 1 ton anvil  |
| 2 ton anvil  |
+--------------+

上述表達式希望找出一個以一個數(包括以小數點開始的數)開始的產品。

注意: ^在集合中還可以用於否定該集合。

5. 計算字段和數據處理函數

5.1 計算字段

如果想在一個字段中既顯示公司名,又顯示公司地址,但這兩個信息一般在一張表的兩個列中
有時候我們需要直接從數據庫中檢索出轉換、計算或格式化過的數據,而不是先檢索出原始數據然後在客戶端重新格式化,這就是計算字段發揮作用的所在了。計算字段是運行時在SELECT語句內創建的
字段(field):基本上與列(column)意思相同,經常互換使用,不過數據庫列一般稱爲列,而術語字段常用在計算字段的連接上。

5.1.1 拼接字段/別名

拼接(concatenate):將值聯結到一起構成單個值。
別名(alias):是一個字段或值的替代名,除了可以用在計算字段中外,常見的用途還包括在實際的表列名包含不符合規定的字符(如空格)時重新命名它,在原來的名字容易誤解時擴充它,等。

mysql> SELECT Concat(vend_name, '(', vend_country, ')') AS vend_title
       FROM vendors ORDER BY vend_name;
+------------------------+
| vend_title             |
+------------------------+
| ACME(USA)              |
| Anvils R Us(USA)       |
| Furball Inc.(USA)      |
+------------------------+

Mysql中使用Concat函數拼接兩個列(注意多數DBMS使用+或||拼接)

5.1.2 執行算術計算
計算字段的另一常見用途是對檢索出的數據進行算術計算,例如我們需要根據數量和單價計算總價:

mysql> SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price
    -> FROM orderitems WHERE order_num = 20005;
+---------+----------+------------+----------------+
| prod_id | quantity | item_price | expanded_price |
+---------+----------+------------+----------------+
| ANV01   |       10 | 5.99       | 59.90          |
| ANV02   |        3 | 9.99       | 29.97          |
| TNT2    |        5 | 10         | 50.00          |
| FB      |        1 | 10         | 10.00          |
+---------+----------+------------+----------------+
4 rows in set

5.2 數據處理函數

雖然函數可移植性不強,但多數SQL支持以下類型的函數:
用於處理文本串的文本函數(如刪除或填充值,轉換大小寫);
對數值數據進行算術操作的數值函數(如返回絕對值,平方根);
處理日期和時間值並提取特定成分的日期&時間函數(如返回日期之差,年,月);
返回DBMS正使用的特定信息的系統函數(如返回用戶登錄信息,檢查版本)。

5.2.1 文本處理函數

mysql> SELECT vend_name, Upper(vend_name) AS vend_name_upcase
    -> FROM vendors ORDER BY vend_name;
+----------------+------------------+
| vend_name      | vend_name_upcase |
+----------------+------------------+
| ACME           | ACME             |
| Anvils R Us    | ANVILS R US      |
+----------------+------------------+

5.2.2 日期和時間處理函數
日期和時間採用了特定的數據類型和格式存儲,以便能快速和有效的排序和過濾,並節省物理存儲空間。
在分析數據時,通常我們都需要用日期對數據進行過濾,這裏要注意的是,日期的標準格式永遠都是yyyy-mm-dd。

mysql> SELECT * FROM orders WHERE order_date = '2005-09-01';
mysql> SELECT * FROM orders WHERE Date(order_date) = '2005-09-01';
+-----------+---------------------+---------+
| order_num | order_date          | cust_id |
+-----------+---------------------+---------+
|     20005 | 2005-09-01 00:00:00 |   10001 |
+-----------+---------------------+---------+

上述代碼中,兩條語句都會返回同樣的結果,但是在通過日期進行過濾時,建議使用Date()函數,因爲它們只有在時間值都爲00:00:00的時候纔等價。而當某個值爲2005-09-01 11:30:05時,上面的語句就無法匹配出來。
Date()函數會提取指定列的日期部分,類似的是Time()函數提取指定列的時間部分。

-- 以下兩行代碼都可以提取時間爲2005年9月的所有訂單
SELECT * FROM orders WHERE Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30';
SELECT * FROM orders WHERE Year(order_date)=2005 AND Month(order_date)=09;

5.2.3 數值處理函數
主要用於代數、三角或幾何運算,如Abs(), Cos(), Exp()等。

關於更多常用函數可以查看這篇博客:https://blog.csdn.net/yyc794990923/article/details/77228304

5.3 聚集函數

很多時候,我們需要的是表中數據的彙總值而不是實際數據本身,那麼返回實際數據的列表就是對時間和處理資源的浪費,因此我們可以使用SQL的聚集函數,MySQL中包含五種聚集函數:COUNT(), AVG(), SUM(), MAX(), MIN(),此外還有計算標準差的聚集函數等。

聚集函數(aggregate function):運行在行組上,計算和返回單個值的函數。

-- 計算products表中所有產品的平均價格
SELECT AVG(prod_price) AS avg_price FROM products;

--返回customers表中客戶的總數
SELECT COUNT(*) AS num_cust FROM customers;

--返回具有郵件地址的客戶總數
SELECT COUNT(cust_email) AS num_cust FROM customers;

-- 返回列中的最大值和最小值,注意可以通過逗號組合聚集函數
SELECT MAX(prod_price) AS max_price, MIN(prod_price) AS min_price FROM products;

-- 返回指定列的總和
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;

注意:
AVG只能作用於單個列,如要獲取多個列的平均值,必須使用多個AVG()函數。
AVG()忽略值爲NULL的行,COUNT(*)對錶行進行計數,不管列中包含的是否NULL值,COUNT(column)則忽略值爲NULL的行,MAX(),MIN(),SUM()也忽略值爲NULL的行。
MAX和MIN除了可以對數值取極值,對文本也是可以的。
如上面SUM的例子所示,利用標準的算術操作符,所有聚集函數都可以對多個列的計算值進行聚集。

5.3.1 聚集不同值
以上5個聚集函數,都可以在計算中指定ALL參數(默認)或DISTINCT參數。以取均值爲例,ALL參數代表默認是對所有的值進行平均,DISTINCT則表示對所有的唯一值取平均。

SELECT AVG(DISTINCT prod_price) AS avg_price FROM products WHERE vend_id=1003;

5.3.2 組合聚集函數

mysql> 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;
+-----------+-----------+-----------+-----------+
| num_items | price_min | price_max | price_avg |
+-----------+-----------+-----------+-----------+
|        14 | 2.5       | 55        | 16.133571 |
+-----------+-----------+-----------+-----------+

如代碼所示,可以使用SELECT語句一次執行多個聚集計算並返回多個值。

5.4 分組數據

5.4.1 通過GROUP BY 創建分組

mysql> SELECT vend_id, COUNT(*) AS num_prods FROM products GROUP BY vend_id;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
|    1001 |         3 |
|    1002 |         2 |
|    1003 |         7 |
|    1005 |         2 |
+---------+-----------+

從上述代碼可以看出,GROUP BY子句指示MySQL按vend_id排序並分組數據,這導致對每個vend_id而不是整個表計算num_prods一次。
用Excel的數據透視表來理解就是,SELECT後跟的是行名和值,GROUP BY後指定行名。

以下是一些使用GROUP BY的重要規定:

  • GROUP BY子句可以包含任意數量的列,也就是可以嵌套。
  • 如果在GROUP BY中嵌套了分組,數據將在最後規定的分組上進行彙總。
  • GROUP BY子句中列出的每個列都必須是檢索列或有效的表達式(但不能是聚集函數)。如果在SELECT中使用表達式,則必須在GROUP BY中指定相同的表達式。不能使用別名。
  • 除聚集計算語句外,SELECT語句中每個列都必須在GROUP BY子句中給出。
  • 如果分組列中具有NULL值,則NULL值將作爲一個分組返回。
  • GROUP BY子句必須出現在WHERE子句之後,ORDER BY子句之前。
  • 使用WITH ROLLUP可以得到每個分組及每個分組彙總級別的值。

5.4.2 使用HAVING過濾分組
在我們得到分組數據後,有時候我們並不是每個分組的數據都想要,比如我們想要列出至少有兩個訂單的所有顧客。爲了得到這種數據,必須基於完整的分組(HAVING)而不是個別的行(WHERE)進行過濾。

HAVING支持WHERE的所有的操作符

SELECT cust_id, COUNT(*) AS orders FROM orders GROUP BY cust_id HAVING COUNT(*) >=2;

上述代碼返回了至少有兩個訂單的所有顧客列表,這裏是不能使用WHERE的。
那麼HAVING 和 WHERE的區別怎麼理解呢:WHERE 是在數據分組前進行過濾,HAVING是在數據分組後進行過濾。也就是說WHERE排除的行不包括在分組中,這可能會改變計算值,從而影響HAVING過濾的分組結果。

事實上,WHERE和HAVING是可以同時使用的,比如我們希望返回具有數量2個以上,價格10以上的產品的供應商:

SELECT vend_id, COUNT(*) AS orders 
FROM products WHERE prod_price >=10 
GROUP BY vend_id HAVING COUNT(*) >=2;

5.4.3 SELECT子句順序

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

注意:雖然GROUP BY 的輸出確實是以分組順序輸出的,但情況並不總是這樣,而且也不符合SQL規範,因此在提取數據時仍然應該提供明確的ORDER BY子句,即使其效果等同於GROUP BY也是如此。

6.多表查詢

6.1 子查詢

查詢(query):任何SQL語句都是查詢。但此術語一般指SELECT語句
子查詢(subquery):嵌套在其他查詢中的查詢

6.1.1利用子查詢進行過濾

假設我們有3個表:

  • orders表存儲訂單號,客戶ID
  • orderitems存儲訂單號,物品信息
  • customers 存儲客戶ID,客戶信息

又假設我們想要列出所有訂購物品TNT2的客戶信息,那麼可能的做法是:

  • 在orderitems中檢索所有TNT2的訂單號
  • 根據步驟1的訂單號在orders中檢索客戶ID
  • 根據步驟2的客戶ID在customers中檢索客戶信息

上述3步可以寫成3條SELECT語句,把上一步驟的結果作爲下一步的WHERE子句的篩選條件。
自然也可以通過子查詢把3步組合成一條語句。

-- 分成三步的版本
SELECT order_num FROM orderitems WHERE prod_id = 'TNT2';
SELECT cust_id FROM orders WHERE order_num IN (20005, 20007);
SELECT cust_name, cust_contact FROM customers WHERE cust_id IN (10001, 10004);
-- 通過子查詢一步完成的版本
mysql> SELECT cust_name, cust_contact
    -> FROM customers
    -> WHERE cust_id IN (SELECT cust_id
    -> 			 FROM orders
    -> 			 WHERE order_num IN(SELECT order_num
    -> 					    FROM orderitems
    -> 					    WHERE prod_id = 'TNT2'));

雖然對於能嵌套的子查詢數目沒有限制,但由於性能限制,不要嵌套太多
子查詢中的SELECT語句必須具有和上一級的WHERE子句相同數目的列,通常都是單個列
子查詢一般與IN操作符結合使用,但也可以用於測試等於(=),不等於(<>)等

6.1.2 將子查詢作爲計算字段
使用子查詢的另一方法是創建計算字段。
例如還是對於上面的orders,orderitems,customers三個表,
假設需要查詢customers中每個客戶的訂單總數,我們需要:

  • 從customers中檢索客戶列表
  • 對於檢索出的每個客戶,統計其在orders表中的訂單數目
-- 比如我們得到客戶列表後,對其中的10001客戶訂單進行計數
SELECT COUNT(*) AS orders FROM orders WHERE cust_id = 10001;

爲了對每個客戶進行COUNT(*)計算,應該將COUNT(*)作爲一個子查詢,如下代碼所示:

mysql> SELECT cust_name,
    -> 	      cust_state,
    -> 	      (SELECT COUNT(*)
    -> 	       FROM orders
    ->         WHERE orders.cust_id = customers.cust_id) AS orders
    -> FROM customers ORDER BY cust_name;
+----------------+------------+--------+
| cust_name      | cust_state | orders |
+----------------+------------+--------+
| Coyote Inc.    | MI         |      2 |
| E Fudd         | IL         |      1 |
| Mouse House    | OH         |      0 |
| Wascals        | IN         |      1 |
| Yosemite Place | AZ         |      1 |
+----------------+------------+--------+

上述子查詢對customers.cust_id中每個值都執行了一次,一共執行了5次。這種類型的子查詢也叫作相關子查詢,即涉及外部查詢的子查詢。任何時候列名有多意性,就必須使用類似WHERE orders.cust_id = customers.cust_id的語法(表名和列名由一個句點分隔)。如果不指定表名的話,結果就會完全不對。

這裏要注意的是,本小節中使用的子查詢方法雖然有效,但並不是最有效的方法,後面的表聯結效率更高。

6.2 表聯結

SQL最強大的功能之一就是能在數據檢索查詢的執行中聯結(join)表。如何在一張表中讀取數據是相對簡單的,但是在真正的應用中經常需要從多個數據表中讀取數據.

在能有效使用聯結之前,必須明白關係型數據庫和關係表的基本原理。關係表把信息分解成多個表,一類數據一個表。各表通過某些常用的值互相關聯。每一個表中都有一個主鍵,也可能有其他表的主鍵,其他表的主鍵稱之爲外鍵,通過外鍵可以把兩個表相關聯。這麼做可以減少數據重複,避免大量修改,可擴展性更好。

聯結分爲幾種:

  • INNER JOIN(內聯結,或等值聯結):獲取兩個表中字段關係匹配的記錄
  • LEFT JOIN(左聯結):獲取左表所有記錄,即使右表沒有對應匹配的記錄。
  • RIGHT JOIN(右聯結):獲取右表所有記錄,即使左表沒有對應匹配的記錄。
  • CROSS JOIN(交叉聯結):也叫笛卡爾積,檢索出行的數目是第一個表中的行數乘以第二個表中的行數。

6.2.1 內聯結

mysql> SELECT vend_name, prod_name, prod_price 
	-> FROM vendors, products
    -> WHERE vendors.vend_id = products.vend_id
    -> ORDER BY vend_name, prod_name;
mysql> SELECT vend_name, prod_name, prod_price
    -> FROM vendors INNER JOIN products
    -> ON vendors.vend_id = products.vend_id;

上面兩條代碼使用的都是內聯結,一個用的是WHERE 一個用的是 INNER JOIN & ON,它們返回的結果是相同的,但是根據規範,應該使用INNER JOIN語法。

SQL對一條SELECT語句中可以聯結的表的數目沒有限制,因此我們可以使用多表聯結更加高效的完成上一小節用子查詢完成的任務:

mysql> SELECT cust_name, cust_contact
    -> FROM customers, orders, orderitems
    -> WHERE customers.cust_id = orders.cust_id
    -> 	 AND orders.order_num = orderitems.order_num
    -> 	 AND orderitems.prod_id = 'TNT2';

6.2.2 使用表別名
前面我們已經將別名用於列名和計算字段,其實SQL還允許給表名起別名,這樣有兩個作用:縮短SQL語句,允許單條SELECT語句中多次使用相同的表。

例如我們將上一條代碼使用別名:

mysql> SELECT cust_name, cust_contact
    -> FROM customers AS c, orders AS o, orderitems AS oi
    -> WHERE c.cust_id = o.cust_id
    ->   AND o.order_num = oi.order_num
    ->   AND oi.prod_id = 'TNT2';

表別名不僅能用於WHERE子句,還可以用於SELECT的列表,ORDER BY子句以及語句的其他部分。但要注意的是,表別名智能在查詢執行中使用,與列別名不一樣,表別名不返回到客戶端。

6.2.3 自聯結
自聯結即某個表自己與自己聯結,因此剛剛提到的表別名可以允許單條SELECT語句中多次使用相同的表就派上用場了。
假設你發現某個ID爲DTNTR的物品存在問題,想知道這個供應商的其他物品是否也有問題。
那麼你就需要:

  • 先在products表中找到DTNTR的供應商ID
  • 繼續在products表中找到這個供應商的其他產品

你可以通過子查詢來做到,或分兩次查詢也可以:

mysql> SELECT prod_id, prod_name
    -> FROM products
    -> WHERE vend_id = (SELECT vend_id
    -> 			FROM products
    -> 			WHERE prod_id = 'DTNTR');

而使用表聯結的效率會更高:

mysql> 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';

6.2.4 自然聯結
自然聯結排除多次出現,使每個列只返回一次。
迄今爲止,我們建立的內部聯結都是自然聯結,很可能永遠也用不到不是自然聯結的內聯結,所以不過多介紹。

6.2.5 外聯結
有時候我們需要在返回的結果包含沒有關聯行的那些行,這種類型的聯結叫做外聯結,如:列出所有客戶及其訂單,包括沒有訂單的客戶

外聯結使用OUTER JOIN來指定聯結的類型,並且必須要使用RIGHT 或 LEFT 來指定包括其所有行的表。

  • LEFT OUTER JOIN選擇左邊的表所有的行
  • RIGHT OUTER JOIN選擇右邊的表所有的行
  • 可以簡寫爲LEFT JOIN 或 RIGHT JOIN,因爲它們默認是外聯結
  • 左右聯結唯一的差別是所關聯表的順序不同,兩者可以顛倒後完全互換
    在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

繼續上面的例子,列出所有客戶及其訂單,包括沒有訂單的客戶:

mysql> SELECT customers.cust_id, orders.order_num
    -> FROM customers LEFT OUTER JOIN orders
    -> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10002 | NULL      |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
6 rows in set

我們可以看到,所有的客戶都列出來了,包括還沒有訂單的客戶。

6.2.6 使用帶聚集函數的聯結
假設我們要檢索所有客戶及每個客戶所下的訂單數:

-- 使用內聯結
mysql> SELECT customers.cust_name, customers.cust_id, COUNT(orders.order_num) AS num_order
    -> FROM customers INNER JOIN orders
    -> ON customers.cust_id = orders.cust_id
    -> GROUP BY customers.cust_id;

這裏要使用GROUP BY,不然就會聚集爲一個數值了。

-- 使用外聯結
mysql> SELECT customers.cust_name, customers.cust_id, COUNT(orders.order_num) AS num_order
    -> FROM customers LEFT JOIN orders
    -> ON customers.cust_id = orders.cust_id
    -> GROUP BY customers.cust_id;
+----------------+---------+-----------+
| cust_name      | cust_id | num_order |
+----------------+---------+-----------+
| Coyote Inc.    |   10001 |         2 |
| Mouse House    |   10002 |         0 |
| Wascals        |   10003 |         1 |
| Yosemite Place |   10004 |         1 |
| E Fudd         |   10005 |         1 |
+----------------+---------+-----------+

7.組合查詢及全文本搜索

7.1使用UNION操作符

組合查詢是指從一個或多個表中執行多個查詢(多條SELECT語句),並將結果作爲單個查詢結果集返回,這些組合查詢被稱爲並(union)。

通常有兩種情況需要用到組合查詢:

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

其實組合查詢和多個WHERE條件(通過OR)完成的工作在多數情況下相同,但在不同的查詢中性能不一樣,因此可以辯證的選擇。

組合查詢中需要用到UNION關鍵字,它的使用很簡單,只需要在每條SELECT語句中間放上關鍵字UNION就可以了。

假設我們需要返回一個列表,其中包括:價格小於5的物品,和供應商10001和10002生產的所有物品(不考慮價格)。

mysql> 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);

使用WHERE子句也能做到:

mysql> SELECT vend_id, prod_id, prod_price
    -> FROM products
    -> WHERE prod_price < 5 OR vend_id IN (1001,1002);
+---------+---------+------------+
| vend_id | prod_id | prod_price |
+---------+---------+------------+
|    1001 | ANV01   | 5.99       |
|    1001 | ANV02   | 9.99       |
|    1001 | ANV03   | 14.99      |
|    1003 | FC      | 2.5        |
|    1002 | FU1     | 3.42       |
|    1002 | OL1     | 8.99       |
|    1003 | SLING   | 4.49       |
|    1003 | TNT1    | 2.5        |
+---------+---------+------------+

在這個簡單的例子中,使用UNION可能比WHERE更復雜,但在更復雜的過濾條件或者從哪個多個表檢索數據時,使用UNION可能會更簡單。

使用UNION的規則:
①UNION必須由兩條或以上的SELECT語句組成,語句間用關鍵字UNION分隔
②UNION中每個查詢必須包含相同的列,表達式或聚集函數(不過順序不一定要一致)
③列數據類型必須兼容:雖然不必完全相同,但必須是DBMS可以隱含地轉換的類型

7.2 包含或取消重複的行

在上面的查詢中,其實有一條數據是同時滿足兩個條件的,而UNION默認會把重複的行給取消掉,所以本來第一條SELECT語句返回4行,第二條返回5行,而最終只返回8行。

如果想返回所有匹配的行而不取消重複的行的話,可以使用UNION ALL而不是UNION,而這個功能也正是WHERE無法做到的。

mysql> SELECT vend_id, prod_id, prod_price
    -> FROM products
    -> WHERE prod_price < 5
    -> UNION ALL
    -> SELECT vend_id, prod_id, prod_price
    -> FROM products
    -> WHERE vend_id IN (1001, 1002);

7.3 對組合查詢進行排序

如果想要對輸出結果進行排序,在UNION組合只能使用一條ORDER BY子句,它必須出現在最後一條SELECT語句之後。對於結果集,不存在用一種方式排序一部分,而又用另一種方式排序另一部分的情況,因此不允許使用多條ORDER BY子句。

mysql> 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)
    -> ORDER BY vend_id, prod_price;

注意:爲了表述簡單,上文組合的都是相同的表的查詢,其實通常用於不同的表的組合查詢更多。

7.4 全文本搜索

爲什麼要使用全文本搜索?
其實有類似於全文本搜索的功能:
1、LIKE關鍵字:利用通配符操作匹配文本,使用LIKE,能夠查找包含特殊值或部分值的行(不管這些值在什麼位置)。
2、正則表達式:基於文本搜索的正則表達式可以編寫查找所需行的更復雜的匹配模式。
儘管以上兩種搜索匹配機制很有用,但是存在這麼幾個重要的限制:

  • 性能–通配符和正則表達式匹配通常要求MYSQL嘗試匹配表所有行(這些搜索極少使用變索引)。因此隨着表數據的增加,這些搜索可能非常耗時;
  • 明確控制–使用通配符和正則表達式很難明確地控制匹配什麼和不匹配什麼。例如指定一個詞必須匹配,另一個詞必須不匹配。
  • 智能化的結果–通配符和正則表達式的搜索功能儘管強大,但是他們並不能提供一種智能化的選擇結果的方法。如:一個特殊值的搜索會返回所有匹配的行,而不區分單個匹配的行與多個匹配的行(按照可能是更好匹配來排列搜索結果);一個特殊值的搜索將不會找出不包含該詞組但包含其他相關詞的行。

所有的這些限制,用全文本搜索都能得到很好的解決,而且在使用全文本搜索功能時,MYSQL不需要分別查看每個行,不需要分別分析和處理每個詞、MYSQL創建指定列中各詞的一個索引,搜索可以針對這些詞進行,因此效率更高。

我們在使用MySQL時,一般會用到兩種引擎--MyISAM和InnoDB,MyISAM支持全文本搜索,但不支持事務處理;而InnoDB支持事務處理,但不支持全文本搜索,因此當你要在某個表(一般是包含長文本字段的表)中使用全文本搜索功能時,選擇引擎的時候就要選MyISAM了。

8.對數據庫進行增、刪、改

8.1 插入數據

毫無疑問,SELECT是最常用的語句,此外還有3個經常使用的SQL語句需要學習,第一個就是INSERT,插入可以用以下幾種方式進行:

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

8.1.1 插入一行或一部分

mysql> INSERT INTO customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email)
    -> VALUES('Pep','100 Steet','LA','CA','90046','USA',NULL,NULL);

需要注意的是:

  • 可以不給出列名,但是這樣做值就必須嚴格按照順序給出,容易出錯,因此不推薦
  • 給出列名後,列不一定需要按照表的順序排列,只要和值的順序對應即可
  • 上代碼中,有兩行給的是NULL值,那麼可以在插入時省略這兩列
  • 可以省略的前提是必須滿足:該列定義允許NULL值 或 設置了默認值,如果不滿足這兩個條件之一,那麼就會報錯。
  • INSERT,UPDATA,DELETE操作可能會很耗時,而且可能會降低等待中的SELECT語句的性能。一般來說數據檢索是最重要的,因此可以使用INSERT LOW_PRIORITY INTO語句,插入LOW_PRIORITY關鍵字後,INSERT語句的優先級會降低。

8.1.2 插入多行
插入多行有兩種方法,比較笨的方法是輸入 多條INSERT 語句。
而如果插入的行都是屬於相同的列的話,那麼就可以合併爲一條INSERT語句:
合併之後不僅方便,性能也會提高

mysql> INSERT INTO customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
    -> VALUES('Pep','100 Steet','LA','CA','90046','USA'),
    ->       ('Mar','42 Street', 'NY', 'NY','11212','USA');

8.1.3 插入檢索的數據
INSERT除了可以用來向表插入一個指定列值的行,還可以利用它將一條SELECT語句的結果插入表中,這就是所謂的INSERT SELECT。

假設你想從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;

需要注意的是:

  • custnew中的cust_id不能與customers表中的cust_id有重複值,因爲如果主鍵值重複,INSERT操作將會失敗
  • 上述語句會插入多少行有賴於custnew中youduoshaohang有多少行,如果這個表爲空,那麼就不會有任何行被插入
  • 其實如果需要的話,可以省略導入cust_id列以避免出錯
  • 在上面語句中,爲簡單起見在INSERT和SELECT中使用了相同的列名,事實上SQL根本不關心列名是否一致,它是按照順序進行導入的
  • SELECT語句可包含WHEREA子句以過濾插入的數據

8.2 更新數據

爲了更新(修改)表中的數據,可使用UPDATE語句,可以採用兩種方式使用:

  • 更新表中特定行,帶WHERE
  • 更新表中所有行,不帶WHERE

假設需要更新某個客戶的郵件地址:

mysql> UPDATE customers
    -> SET cust_email = '[email protected]'
    -> WHERE cust_id = 10005;
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0

或者需要更新客戶的名字和郵件:

mysql> UPDATE customers
    -> SET cust_name = 'Food',
    ->     cust_email = '[email protected]'
    -> WHERE cust_id = 10005;

要注意的是:

  • 一定要用WHERE過濾條件,不然所有行被都改了
  • 如果想要刪除某條數據,SET其爲NULL值即可
  • 在UPDATE中可以使用子查詢,用檢索出的數據來更新
  • 如果在更新多行的過程中某一個出現錯誤,整個UPDATE都會被取消,如果希望即使發生錯誤也繼續更新的話,可以使用IGNORE關鍵字:UPDATE IGNORE customers

8.3 刪除數據

爲了從一個表中刪除數據,可使用DELETE語句,可以採用兩種方式使用:

  • 從表中刪除特定行,指定WHERE
  • 從表中刪除所有行,不指定WHERE

假設需要從表中刪除一行:

mysql> DELETE FROM customers
    -> WHERE cust_id = 10006;
Query OK, 1 row affected

要注意的是:

  • DELETE語句不需要列名或通配符。它刪除整行而不是刪除列,爲了刪除指定的列,請使用UPDATE語句。
  • DELETE從表中刪除行,但是不刪除表本身
  • 如果想要刪除所有行,建議不使用DELETE,而是TRUNCATE TABLE語句。
  • 對UPDATE或DELETE使用WHERE子句前應當先用SELECT語句進行測試,以防編寫的WHERE子句不正確。

8.4 創建和操縱表

8.4.1創建表
一般來說,有兩種創建表的方法:使用CREATE TABLE語句,或直接使用交互式的工具進行創建和管理表,如Navicat,會非常直觀和方便。

下面我們用一個樣例代碼來介紹一下需要注意的地方:

CREATE TABLE customers
(
	cust_id		int			NOT NULL  AUTO INCREMENT,
	cust_name   char(50)    NOT NULL,
	cust_city	char(50)	NULL, DEFAULT ‘NY’
	cust_email  char(255)	NULL,
	PRIMARY KEY(cust_id)
) ENGINE=InnoDB;

從示例中可以看出,表名緊隨CREATE TABLE之後,列的定義在括號中,以逗號分隔,包括列名,數據類型,是否允許NULL,需要了解的是:

  • 包含關鍵字NOT NULL後,就會阻止插入的數據省略了此列,那麼就會阻止並報錯
  • NULL是沒有值,而不是空值,因此在NOT NULL中輸入‘’是允許的(引號間爲空)
  • 主鍵必須唯一,且只能使用NOT NULL的列
  • AUTO INCREMENT告訴MySQL,本列每增加一行時自動增量。每個表只允許一個AUTO INCREMENT,而且它必須被索引(如成爲主鍵)。
  • 在INSERT中可以使用一個唯一的值代替AUTO INCREMENT默認將賦予的值,然後後面增加的列就會在這個值的基礎上遞增。 此外,可以使用SELECT last_insert_id()獲取當前AUTO INCREMENT的值。
  • 用DEFAULT指定默認值,而不是NULL,特別是用於計算或數據分組的列
  • MySQL有多種引擎,如InnoDB(事務處理,不支持全文本搜索),MEMORY(適合臨時表),MyISAM(全文本搜索,不支持事務處理),創建表時可以對引擎進行選擇也可以使用默認的。但要注意,如果數據庫中不同的表使用不同的引擎,外鍵是不能跨引擎的,即使用某個引擎的表不能引用具有不同引擎的表的外鍵。

本文中的例子都是使用單個列作爲主鍵的,其實是可以使用多列作爲主鍵,如果使用多個列,那麼這些列的組合必須唯一,創建多個列作爲主鍵的示例:

CREATE TABLE orderitems
(
	order_num		int		NOT NULL,
	order_item      int     NOT NULL,
	prod_id			char(10) NULL
	PRIMARY KEY(order_num, order_item)
) ENGINE=InnoDB;

8.4.2更新表
更新表包括增加列或刪除列,使用ALTER TABLE 語句,但是理想情況下,當表中存儲數據後,該表就不應該再被更新,而應該在表的設計過程中考慮清楚。

給表增加一個列:

mysql> ALTER TABLE vendors
    -> ADD vend_phone CHAR(20);

刪除一個列:

mysql> ALTER TABLE vendors
    -> DROP COLUMN vend_phone;

ALTER COLUMN還有一個常見用途是定義外鍵

ALTER TABLE orders
ADD CONSTRAINT fk_orders_customers FOREIGN KEY (cust_id)
REFERENCES customers(cust_id);

8.4.3 刪除表,重命名錶

刪除整個表,要注意操作沒有確認也不能撤銷:

DROP TABLE customers2;

重命名錶:

RENAME TABLE customers2 TO customers3;

重命名多個表:

RENAME TABLE customers2 TO customers3,
			 vendors2 TO vendors3,
			 products2 TO products3;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章