SQL基礎知識

一 瞭解SQL

1 數據庫基礎

  • 數據庫(database):保存有組織的數據容器。
  • 表(table):某種特定類型數據的結構化清單。表名實際由數據庫名和表名共同組成惟一字符串。
  • 列(column):表中的一個字段。全部表都是由一個或多個列組成。
  • 數據類型(datatype):所容許的數據類型。每一個列都有相應的數據類型,限制或容許了該列中存儲的數據。
  • 行(row):表中的一個記錄。
  • 主鍵(primary key):一列或一組列,值可以惟一標識表中的每一行。
    • 惟一:任意兩行不能具備相同主鍵值
    • 非空:每一行都必須有一個主鍵值且不爲NULL
    • final:主鍵列的值不容許修改或更新
    • 非重用:主鍵值不能重用

2 什麼是SQL

SQL爲結構化查詢語言(Structured Query Language)的縮寫。SQL是一種專門用來與數據庫溝通的語言。html

二 檢索數據

1 相關概念

  • 關鍵字(keyword):做爲SQL組成部分的保留字。不能做爲表或列名。
  • 大小寫:SQL語句不區分大小寫,能夠將關鍵字用大寫表示區分。
  • 結束:SQL語句的結束爲分號(;)。

2 檢索列

-- 檢索單個列
SELECT prod_name FROM Products;

-- 檢索多個列
SELECT prod_id,prod_name,prod_price FROM Products;

-- 檢索全部列
SELECT * FROM Products;

注:檢索全部列會下降檢索和應用程序的性能。java

3 檢索不一樣的值(去重)

SELECT DISTINCT vend_id FROM Products;

-- DISTINCT不能部分使用,下面的SQL只有當vend_id和prod_name都相同的兩行纔不會被檢出。
SELECT DISTINCT vend_id,prod_name FROM Products;

4 限制結果(各類數據庫的實現不相同)

-- 前5條
-- MYSQL SQLite PostgreSQL
SELECT prod_name FROM Products LIMIT 5;

-- Oracle
SELECT prod_name FROM Products WHERE ROWNUM <= 5

/*
    分隔條
*/

-- 跳過前5條
-- MYSQL SQLite PostgreSQL
SELECT prod_name FROM Products LIMIT 5 OFFSET 5;

-- MYSQL
SELECT prod_name FROM Products LIMIT 5, 5;

-- Oracle
SELECT prod_name FROM Products WHERE ROWNUM > 5 and ROWNUM <= 10;

三 排序檢索數據

1相關概念

  • 子句(clause):有些子句是必需的(SELECT ... FROM tableName;),有些則不是(ORDER BY)。

2 排序數據(默認升序)

SELECT prod_name FROM Products ORDER BY prod_name;

注1:ORDER BY只能在SELECT語句最後一條子句。
注2:ORDER BY所使用的列不須要是顯示而選擇的列,能夠選擇非檢索的列。mysql

3 按照多個列排序

-- 先按照prod_price排序,當prod_price相同時,才按照prod_name排序。
SELECT prod_id,prod_price,prod_name FROM Products ORDER BY prod_price,prod_name;

-- 列位置爲檢出的位置。
SELECT prod_id,prod_price,prod_name FROM Products ORDER BY 2,3;

-- 先按照prod_price降序排,當prod_price相同時,才按照prod_name升序排。
SELECT prod_id,prod_price,prod_name FROM Products ORDER BY prod_price DESC,prod_name;

四 過濾數據

1 相關概念

  • 過濾條件:數據庫中包含大量數據,不多須要檢索表中的全部行,因此一般會只檢索部分數據,檢索部分數據的條件。

2 WHERE子句

SELECT prod_name,prod_price FROM Products WHERE prod_price = 3.49;

3 WHERE子句操做符

(1)子句操做符

操做符 說明
= 等於
<> 或 != 不等於
< 小於
> 大於
!< 不小於
!> 不大於
>= 大於等於
<= 小於等於
BETWEEN 兩值之間
IS NULL 爲NULL

(2)過濾數據

-- 檢查單個值
SELECT prod_name,prod_price FROM Products WHERE prod_price < 10;

-- 不匹配檢查
SELECT vend_id,prod_name FROM Products WHERE vend_id != 'DLL01';

-- 範圍檢查
SELECT prod_name,prod_price FROM Products WHERE prod_price BETWEEN 5 and 10;

-- 空值檢查
SELECT prod_name FROM Products WHERE prod_price IS NULL;

五 高級數據過濾

1 相關概念

  • 操做符(operator):用來鏈接或改變WHERE子句中子句的關鍵字。
  • AND:檢索知足全部給定過濾條件的行。
  • OR:檢索知足任一給定過濾條件的行。
  • 圓括號:任什麼時候候使用具備AND和OR操做符的WHERE子句,都應該使用圓括號明確地分組操做符。
  • IN:WHERE子句中用來指定要匹配值的清單的關鍵字。
  • NOT:WHERE子句中用來指定非匹配關鍵字。

2 組合WHERE語句

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

-- OR
SELECT prod_id,prod_price,prod_name FROM Products WHERE vend_id = 'DLLL01' OR vend_id = 'BRS01';

-- 求值順序
SELECT prod_name,prod_price WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01') AND prod_price >=10;


/*
   使用IN的好處
   1.在不少合法選項時,IN操做符更清晰。
   2.在不少AND和OR操做組合使用IN時,求值順序更容器管理
   3.IN操做符比一組OR操做符執行更快
   4.IN能夠包含其餘SELECT語句,可以更動態創建WHERE語句。
*/
-- IN
SELECT prod_name,prod_price FROM Products WHERE vend_id IN ('DLL01','BRS01') ORDER BY prod_name;

-- NOT
SELECT prod_name,prod_price FROM Products WHERE vend_id NOT IN ('DLL01','BRS01') ORDER BY prod_name;

六 用通配符進行過濾

1 相關概念

  • 通配符(wildcard):用於匹配值得一部分的特殊字符。
  • 搜索模式(search pattern):由字面量、通配符或二者組合構成的搜索條件。
  • %通配符:0、1或多個字符,不匹配NULL。
  • _通配符:只匹配單個字符。
  • []通配符:指定一個字符集,必需匹配指定位置的一個字符。

2 LIKE操做符

-- % 通配符:0、1或多個字符,不匹配NULL
SELECT prod_id,prod_name FROM Products WHERE prod_name LIKE 'Fish%';
SELECT prod_id, prod_name ROM Products WHERE prod_name LIKE '%bean bag%';

-- 有些數據庫會用空格來填補字段的內容。如:prod_name爲5個字符,'Fly' -> 'Fly  ',這樣'F%y'就沒法匹配到,解決方法爲函數去除空格或'F%y%'
SELECT prod_name FROM Products WHERE prod_name LIKE 'F%y';

-- 搜索電子郵件地址
WHERE email LIKE 'b%@gmail.com';

-- _通配符:只匹配單個字符。
SELECT prod_id, prod_name FROM Products WHERE prod_name LIKE '__ inch teddy bear';

-- []通配符
SELECT cust_contact FROM Customers WHERE cust_contact LIKE '[JM]%' ORDER BY cust_contact;

七 建立計算字段

1 相關概念

  • 字段(field):基本上與列的意思相同,字段通常與計算字段一塊兒使用。
  • 拼接(concatenate):將值聯結到一塊兒(將一個值附加到另外一個值)構成單個值。
  • 刪除空格函數
    • TRIM():刪除左右空格
    • LTRIM():刪除左空格
    • RTRIM():刪除右空格
  • AS:別名的關鍵字

2 拼接字段

-- 大部分數據庫
SELECT vend_name + ' (' + vend_country + ')' FROM Vendors ORDER BY vend_name;
SELECT vend_name || ' (' || vend_country || ')' FROM Vendors ORDER BY vend_name;

-- MYSQL
SELECT Concat(vend_name,'(',vend_country,')') FROM Vendors ORDER BY vend_name;

3 別名

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

注1:別名能夠是字符串,如:‘vend title’,不夠最後不使用多單詞的字符串,而是使用_分隔的單詞。算法

4 執行算術計算

-- 彙總價格
SELECT prod_id,quantity,item_price,quantity*item_price AS expanded_price FROM OrderItems WHERE order_num = 20008;

八 使用數據處理函數

1 相關概念

  • 可移植性(portable):所編寫的代碼能夠在多個系統上運行。
  • 函數是否應該使用?可使用,但須要有相應的註釋。

2 函數差別

函數 語法
截取字符串 Oracle使用SUBSTR();MySQL使用SUBSTRING(str,start,end)
數據類型轉換 Oracle使用多個函數;MySQL使用CONVERT()
獲取當前時間 Oracle使用SYSDATE;MySQL使用CURDATE()

3 使用

(1)文本處理函數

函數 說明
LEFT(str,len) 返回字符串左邊字符
LENGTH(str) 或 DATALENGTH() 或 LEN() 返回字符串的長度
LOWER(st) UPPER(str) 小寫 大寫
LTRIM(str) TRIM(str) RTRIM(str) 刪除左空格 刪除左右空格 刪除右空格
SOUNDEX() 返回字符串的SOUNDEX值

注1:上述加粗的部分表明MySQL可用函數。
注2:soundex是一個將任何文本串轉換爲描述其語音表示的字母數字模式的算法。sql

SELECT SUBSTRING(prod_name,5,6) AS prod_new_id FROM products;

SELECT LEFT(prod_name,2) AS left_name FROM products;

SELECT LENGTH(prod_name) AS name_length FROM products;

SELECT LTRIM(prod_name) FROM products;

-- 尋找顧客聯繫方式發音爲Y San的顧客
SELECT * FROM customers WHERE soundex(cust_contact) = soundex('Y San');

(2)日期和時間處理函數

-- 尋找order_date爲2012年的數據

-- Oracle
-- 方法1
SELECT order_num FROM Orders WHERE to_number(to_char(order_date, 'YYYY')) = 2012;
-- 方法2
SELECT order_num FROM Orders WHERE order_date BETWEEN to_date('01-01-2012') AND to_date('12-31-2012');

-- MySQL
SELECT order_num FROM Orders WHERE YEAR(order_date) = 2012;

具體列出MySQL的部分日期函數數據庫

函數 說明
now() 獲取當前日期+時間(date + time)
current_timestamp() 獲取當前時間戳
date_format(str,format);str_to_date(str,format) date -> str;str -> date
to_days(date);from_days(days) date -> 天數;天數 -> date
time_to_sec(time);sec_to_time(sec) time -> sec;sec -> time
timediff(time1,time2) datediff(date1,date2) 日期相減
date_add(date,interval expr UNIT) 加exper UNIT
SELECT now()

SELECT CURRENT_TIMESTAMP

-- 2019-02-14 15:22:00
SELECT DATE_FORMAT(now(),'%Y-%m-%d %H:%i:%s')
SELECT STR_TO_DATE('20190214 15:22:00','%Y%m%d %H:%i:%s');

SELECT DATE_ADD(now(),INTERVAL 1 day)

(3)數值處理函數

函數 說明
ABS(x) 絕對值
COS(x) SIN(x)TAN(x) 餘弦值 正弦值 正切值
EXP(x) 指數值
PI() 圓周率
SQRT(x) 平方根
CEIL(x) FLOOR(x) ROUND(x) 最大整數值 最小整數值 四捨五入
TRUNCATE(x,y) 保留y位小數值
RAND() 0~1隨機數
MOD(x,y) x mod y
SELECT ABS(-11);

-- 0.5
SELECT COS(PI()/180*60) 
SELECT SIN(PI()/180*30)

-- 6
SELECT ceil(5.1)
-- 5
SELECT FLOOR(5.1)
-- 5
SELECT ROUND(5.1)

-- 1.12
SELECT TRUNCATE(1.12345,2)

九 彙總數據

1 聚合函數

函數 說明
AVG() 某列的平均值,忽略NULL值
COUNT() 某列的行數
MAX() MIN() 某列最大值 最小值
SUM() 某列求和
SELECT AVG(prod_price) AS avg_price FROM Products WHERE vend_id = 'DLL01';

-- COUNT(*):計算NULL值
-- COUNT(column):忽略NULL值
SELECT COUNT(*) AS num_cust FROM Customers;

-- MAX用於找出最大數值或日期值,若是用於文本則找出排序後的最後一行,忽略NULL值。
SELECT MAX(prod_price) AS max_price FROM Products;

-- 返回訂單中全部物品數量之和,忽略NULL值。
SELECT SUM(quantity) AS items_ordered FROM OrderItems WHERE order_num = 20005;

2 聚合不一樣值

  • 默認聚合全部值
  • DISTINCT則聚合不一樣的值
SELECT AVG(DISTINCT prod_price) AS avg_price FROM Products WHERE vend_id = 'DLL01';

3 組合聚合函數

SELECT COUNT(*) AS num_items,MIN(prod_price) AS price_min,MAX(prod_price) AS price_max, AVG(prod_price) AS price_avgFROM Products;

十 分組數據

1 建立分組

  • GROUP BY子句可包含任意數目的列,於是能夠對分組進行嵌套,進行更細緻的分組。
  • 若是在GROUP BY子句中嵌套了分組,數據將在最後指定的分組上進行彙總。
  • GROUP BY子句中列出的每一列都必須是檢索列或有效的表達式(但不能是彙集函數)。
  • 大多數SQL實現不容許GROUP BY列帶有長度可變的數據類型。
  • SELECT語句中的每一列都必須在GROUP BY子句中給出。
  • 若是分組列中包含具備NULL值的行,則NULL將做爲一個分組返回。
  • GROUP BY子句必須出如今WHERE子句以後,ORDER BY子句以前。
-- 安裝vend_id分組
SELECT vend_id, COUNT(*) AS num_prods FROM Products GROUP BY vend_id;

SELECT vend_id,prod_name,COUNT(vend_id) AS count FROM products GROUP BY vend_id,prod_name;

2 過濾分組

-- 對分組後的結果進行排序
SELECT cust_id, COUNT(*) AS orders FROM Orders WHERE prod_price >= 4 GROUP BY cust_id HAVING COUNT(*) >= 2;

3 分組和排序

ORDER BY GROUP BY
對產出的輸出排序 對行分組,但輸出可能不是分組的順序
任意列均可以(甚至非檢出的列也行) 只可能使用選擇列或表達式列,並且必須使用每一個選擇列表達式
不必定須要 若是與彙集函數一塊兒使用列(或表達式),則必須使用
-- 因爲分組後的順序不肯定,因此最後加上ORDER 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 輸出排序

十一 子查詢

1 相關概念

  • 查詢(Query):任何SQL語句都是查詢,通常指的是SELECT。
  • 子查詢(SubQuery):嵌套在其餘查詢中的查詢。
  • 徹底限定列名:代表.列名

2 子查詢

-- 查看哪位顧客購買了RGAN01商品
SELECT cust_id FROM Orders WHERE order_num 
IN (SELECT order_num
    FROM OrderItems
    WHERE prod_id = 'RGAN01');

注1:子查詢的SELECT語句只能查詢單個列。
注2:子查詢可能影響性能。安全

3 計算字段使用子查詢

-- 查詢顧客購下了多少訂單
SELECT cust_name,
       cust_state,
      (SELECT COUNT(*)
       FROM Orders 
       WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;

十二 聯結表

1 相關概念

(1)關係表

  • Vendors表包含全部供應商信息,每一個供應商佔一行,具備惟一的標識。此標識稱爲主鍵(primary key),能夠是供應商ID或任何其餘惟一值。Products表只存儲產品信息,除了存儲供應商ID(Vendors表的主鍵)外,它不存儲其餘有關供應商的信息。Vendors表的主鍵將Vendors表與Products表關聯,利用供應商ID能從Vendors表中找出相應供應商的詳細信息。
  • 好處:
    • 供應商信息不重複,節省時間和空間。
    • 若是供應商信息變更只須要更新Venders表中的單個記錄。
    • 因爲數據不重複,數據一致,處理數據和生成報表更簡單。

(2)可伸縮

可以適應不斷增長的工做量而不失敗。設計良好的數據庫或應用程序稱爲可伸縮性好(scale well)。服務器

2 建立聯結

(1)笛卡爾積

  • 笛卡兒積(cartesian product):由沒有聯結條件的表關係返回的結果爲笛卡兒積。檢索出的行的數目將是第一個表中的行數乘以第二個表中的行數。
SELECT vend_name,prod_name,prod_price
FROM Venders,Products
WHERE Venders.vend_id = Products.vend_id;

(2)內聯積

-- 等同於上面,只是語法不一樣
SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;

(3)聯結多個表

-- 顯示訂單20007中的物品
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;

-- 子查詢
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 = 'RGAN01'));
-- 聯結方式消除子查詢                  
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 = 'RGAN01

注1:性能:DBMS在運行時關聯指定的每一個表,以處理聯結。這種處理可能很是耗費資源,所以應該注意,不要聯結沒必要要的表。
注2:聯結中表的最大數目:實際上許多DBMS都有限制併發

十三 建立高級聯結

1 概念

  • 表別名:使用AS關鍵字。
  • 聯結(join):自聯結(self-join)、天然聯結(natural join)和外聯結(outer join)。

2 自聯結

-- 子查詢
SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
                    FROM Customers
                    WHERE cust_contact = 'Jim Jones');
                    
-- 子聯結
SELECT cust_id,cust_name,cust_contact
FROM Customers AS c1,Customers AS c2
WHERE c1.cust_name = c2.cust_name
 AND  c2.cust_contact = 'Jim Jones';

注:子聯結比子查詢查詢快得多。ide

3 天然聯結

-- 去除重複列
SELECT C.*, O.order_num, O.order_date,
       OI.prod_id, OI.quantity, OI.item_price
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';

4 外聯結

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

-- 檢索全部顧客及其訂單,不包含沒有訂單的顧客
SELECT Customers.cust_id, Orders.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;
 
-- 全外聯結(full outer join),它檢索兩個表中的全部行並關聯那些能夠關聯的行(惋惜MySQL不支持)
SELECT Customers.cust_id, Orders.order_num
FROM Orders FULL OUTER JOIN Customers
 ON Orders.cust_id = Customers.cust_id;

5 集合函數的聯結

-- 檢索全部顧客及每一個顧客所下的訂單數
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;

十四 組合查詢

1 相關概念

  • 並(union):SQL也容許執行多個查詢(多條SELECT語句),並將結果做爲一個查詢結果集返回。
  • 任何具備多個WHERE子句的SELECT語句均可以做爲一個組合查詢。

2 建立組合查詢

(1)使用UNION

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
 OR cust_name = 'Fun4All';

-- 修改成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';

(2)UNION規則

  • UNION必須由兩條或兩條以上的SELECT語句組成,語句之間用關鍵字UNION分隔。
  • UNION中的每一個查詢必須包含相同的列、表達式或彙集函數
  • 列數據類型必須兼容:類型沒必要徹底相同,但必須是DBMS能夠隱含轉換的類型。

(3)包含或取消重複的行

-- 取消重複: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 ALL
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';

(4)對組合結果排序

在用UNION組合查詢時,只能使用一條ORDER BY子句,它必須位於最後一條SELECT語句以後。

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;

十五 插入數據

1 插入數據

-- 插入完整行
INSERT INTO Customers 
VALUES('1000000006',
       'Toy Land',
       '123 Any Street'
       'New York',
       'NY',
       '11111',
       'USA',
       NULL,
       NULL);

-- 更安全的方式,一樣是填充全部列
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);

-- 插入部分行
-- 省略了cust_contact和cust_email,這兩列容許爲NULL或者有默認值。
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');
       
-- 插入檢索出的數據,從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;

2 從一個表複製到另外一個表

當須要測試新的SQL時,能夠進行該操做複製一張新表進行測試。

-- 建立一個名爲CustCopy的新表,並把Customers表的整個內容複製到新表中
CREATE TABLE CustCopy AS
SELECT * FROM Customers;

-- 只複製表結構
CREATE TABLE CustCopy LIKE Customers;
CREATE TABLE CustCopy AS SELECT * FROM Customers where 1=2

十六 更新和刪除數據

1 更新

UPDATE Customers 
SET cust_contact = 'Sam Roberts',
cust_email = 'kim@thetoystore.com'
WHERE cust_id = '100000000005';

注:更新時不要省略過濾條件,除非你肯定要更新全部行。

2 刪除

DELETE FROM Customers
WHERE cust_id = '100000000005';

-- 清空表
TRUNCATE TABLE Customers;

注:刪除時不要省略過濾條件,除非你肯定要刪除全部行。

十七 建立和操縱表

1 建立表

(1)MySQL數據格式

MySQL數據類型 含義
tinyint(m) 1個字節 範圍(-128~127)
smallint(m) 2個字節 範圍(-32768~32767)
mediumint(m) 3個字節 範圍(-8388608~8388607)
int(m) 4個字節 範圍(-2147483648~2147483647)
float(m.d) 單精度浮點型 8位精度(4字節) m總個數,d小數位,如:float(5,3) 123.4567 => 123.457
double(m,d) 雙精度浮點型 16位精度(8字節) m總個數,d小數位
decimal(m,d) 定點數爲精確值,浮點值爲近似值,m<65 是總個數,d<30 且 d<m 是小數位
char(n) 固定長度,最多255個字符,空格填充,查詢時取出空格,佔n字節。
varchar(n) 可變長度,最多65535個字符,佔實際+1(<=255)或2(大於255)字節但不超過n,可直接建立索引。
tinytext 可變長度,最多255個字符
text 可變長度,最多65535個字符,佔實際+2字節,須要指定前多少字符建立索引。
mediumtext 可變長度,最多2的24次方-1個字符
longtext 可變長度,最多2的32次方-1個字符
date 日期 '2008-12-2'
time 時間 '12:25:36'
datetime 日期時間 '2008-12-2 22:06:44'
timestamp 自動存儲記錄修改時間

注:整型取值範圍若是加了 unsigned,則最大值翻倍

(2)建立

-- 基礎
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_des TEXT NULL
);

-- 使用NULL
CREATE TABLE Orders
(
    order_num INT NOT NULL,
    order_date DATETIME NOT NULL,
    cust_id CHAR(10) NOT NULL
);

CREATE TABLE Vendors
(
    vend_id CHAR(10) NOT NULL,
    vend_name CHAR(50) NOT NULL,
    vend_address CHAR(50) ,
    vend_city CHAR(50) ,
    vend_state CHAR(5) ,
    vend_zip CHAR(10) ,
    vend_country CHAR(50)
);

-- 指定默認值
CREATE TABLE OrderItems
(
    order_num INT NOT NULL,
    order_item INT NOT NULL,
    prod_id CHAR(10) NOT NULL,
    quantity INT NOT NULL DEFAULT 1,
    item_price DECIMAL(8,2) NOT NULL
);

-- 建立Tasks表
CREATE TABLE IF NOT EXISTS Tasks (
    task_id INT(11) NOT NULL AUTO_INCREMENT,
    subject VARCHAR(45),
    start_date DATE,
    end_date DATE,
    description VARCHAR(200),
    PRIMARY KEY (task_id)
) ENGINE = InnoDB;

2 更新表

-- 新增字段
ALTER TABLE Venders
ADD vend_phone CHAR(20);

-- 刪除字段
ALTER TABLE Venders
DROP COLUMN vend_phone;

3 刪除表

DROP TABLE CustCopy;

4 重命名

ALTER TABLE CustCopy RENAME TO CustCopyTest;

十八 使用視圖

1 視圖

(1)基礎

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

(2)好處

  • 重用SQL語句
  • 簡化複雜的SQL操做。
  • 使用表的一部分而不是整個表。
  • 保護數據。授予用戶訪問表的特定部分的權限,而不是整個表的訪問權限。
  • 更改數據格式和表示。

(3)視圖的規則和限制

  • 與表同樣,視圖必須惟一命名
  • 建立視圖,必須具備足夠的訪問權限。
  • 視圖能夠嵌套,便可以利用從其餘視圖中檢索數據的查詢來構造視圖。
  • 許多DBMS禁止在視圖查詢中使用ORDER BY子句。
  • 有些DBMS要求對返回的全部列進行命名,若是列是計算字段,則須要使用別名。
  • 視圖不能索引,也不能有關聯的觸發器或默認值
  • 有些DBMS把視圖做爲只讀的查詢,這表示能夠從視圖檢索數據,但不能將數據寫回底層表。
  • 有些DBMS容許建立這樣的視圖,它不能進行致使行再也不屬於視圖的插入或更新。

2 建立視圖

-- 建立視圖簡化聯結
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; 

SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';

-- 從新格式化檢索出的數據
SELECT CONCAT(RTRIM(vend_name),' (',RTRIM(vend_country),')') 
AS vend_title
FROM Vendors
ORDER BY vend_name;

SELECT *
FROM VendorLocations;

-- 使用視圖和計算字段
CREATE VIEW OrderItemsExpanded AS
SELECT order_num,
        prod_id,
        quantity,
        item_price,
        quantity*item_price AS expanded_price
FROM OrderItems;

SELECT *
FROM OrderItemsExpanded
WHERE order_num = 20008;

十九 存儲過程

1 使用場景

常常會有一些複雜的操做須要多條語句才能完成。例如如下的情形:

  • 爲了處理訂單,必須覈對以保證庫存中有相應的物品。
  • 若是物品有庫存,須要預約,再也不出售給別的人,而且減小物品數據以反映正確的庫存量。
  • 庫存中沒有的物品須要訂購,這須要與供應商進行某種交互。
  • 關於哪些物品入庫(而且能夠當即發貨)和哪些物品退訂,須要通知相應的顧客。

2 好處

  • 經過把處理封裝在一個易用的單元中,能夠簡化複雜的操做。
  • 因爲不要求反覆創建一系列處理步驟,於是保證了數據的一致性。
  • 簡化對變更的管理。若是表名、列名或業務邏輯(或別的內容)有變化,那麼只須要更改存儲過程的代碼。
  • 由於存儲過程一般以編譯過的形式存儲,因此DBMS處理命令的工做較少,提升了性能。
  • 存在一些只能用在單個請求中的SQL元素和特性,存儲過程可使用它們來編寫功能更強更靈活的代碼。

簡言之,簡單、安全、高性能。

3 執行存儲過程

-- EXECUTE 存儲過程(args)
-- 驗證傳遞數據、生成主鍵惟一ID、將新產品插入到Products表中
EXECUTE AddNewProduct( 
    'JTS01',
    'Stuffed Eiffel Tower',
    6.49,
    'Plush stuffed toy with the text La➥Tour Eiffel in red white and blue' 
);

4 建立存儲過程

(1)基礎建立和調用

-- 建立刪除商品的存儲過程
mysql> delimiter $$ 
mysql> CREATE PROCEDURE delete_product(IN p_prod_id int)
    -> BEGIN
    ->      DELETE FROM products
    ->      WHERE prod_id = p_prod_id;
    -> END $$

-- 調用存儲過程
mysql> CALL delete_product(1);

(2)進階

-- 存儲過程體
label1: BEGIN
  label2: BEGIN
    label3: BEGIN
      statements; 
    END label3 ;
  END label2;
END label1

-- 參數說明
CREATE PROCEDURE 存儲過程名([[IN |OUT |INOUT ] 參數名 數據類形...])

-- demo
mysql > DELIMITER // 
mysql > CREATE PROCEDURE update_and_return(INOUT p_prod_id int,IN p_test int)
     -> BEGIN
     -> UPDATE products 
     -> SET vend_id = p_test
     -> WHERE prod_id = p_prod_id;
     -> SELECT vend_id 
     -> FROM products 
     -> WHERE prod_id = p_prod_id;
     -> SET p_prod_id = 4;
     -> END
     -> //
mysql > DELIMITER ;

mysql > SET @Y = 3;
mysql > CALL update_and_return(@Y,100); // 100
mysql > SELECT @Y; // 4

-- 條件語句
mysql > DELIMITER //  
mysql > CREATE PROCEDURE proc2(IN parameter int)  
     -> begin 
     -- declare 聲明變量
     -> declare var int;  
     -> set var=parameter+1;  
     -> if var=0 then 
     -> insert into t values(17);  
     -> end if;  
     -> if parameter=0 then 
     -> update t set s1=s1+1;  
     -> else 
     -> update t set s1=s1+2;  
     -> end if;  
     -> end;  
     -> //  
mysql > DELIMITER ;

-- 循環語句
mysql > DELIMITER //  
mysql > CREATE PROCEDURE proc4()  
     -> begin 
     ->   declare var int;  
     ->   set var=0;  
     ->   while var<6 do  
     ->     insert into t values(var);  
     ->     set var=var+1;  
     ->   end while;  
     -> end;  
     -> //  
mysql > DELIMITER ;

二十 管理事務處理

1 事務處理

(1)定義

事務處理是一種機制,用來管理必須成批執行的SQL操做,保證數據庫不包含不完整的操做結果。

(2)相關術語

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

(3)可回退的語句

事務處理用來管理INSERT、UPDATE和DELETE語句。
不能回退SELECT語句,也不能回退CREATE或DROP操做。

(4)事務的隔離級別

  • 原子性:一個事務中的全部操做,要麼所有完成,要麼所有不完成,不會結束在中間某個環節。
  • 一致性:在事務開始以前和事務結束之後,數據庫的完整性沒有被破壞。
  • 隔離性:數據庫容許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性能夠防止多個事務併發執行時因爲交叉執行而致使數據的不一致。
  • 持久性:事務處理結束後,對數據的修改就是永久的,即使系統故障也不會丟失。

(5)事務併發問題

  • 髒讀:事務A讀取了事務B更新的數據,而後B回滾操做,那麼A讀取到的數據是髒數據
  • 不可重複讀:事務 A 屢次讀取同一數據,事務 B 在事務A屢次讀取的過程當中,對數據做了更新並提交,致使事務A屢次讀取同一數據時,結果不一致。
  • 幻讀:系統管理員A將數據庫中全部學生的成績從具體分數改成ABCDE等級,可是系統管理員B就在這個時候插入了一條具體分數的記錄,當系統管理員A改結束後發現還有一條記錄沒有改過來,就好像發生了幻覺同樣,這就叫幻讀。

小結:不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住知足條件的行,解決幻讀須要鎖表(設置串行化隔離級別)

(6)MySQL事務隔離級別

InnoDB支持下列四種隔離級別;Myisam不支持事務,只支持表鎖。

事務隔離級別 髒讀 不可重複讀 幻讀
讀未提交(read-uncommitted)
不可重複讀(read-committed)
可重複讀(repeatable-read)
串行化(serializable)

(7)InnoDB引擎鎖機制

  • 表鎖

    • 寫鎖:用寫鎖鎖表,會阻塞其餘事務讀和寫。
    • 讀鎖:用讀鎖鎖表,會阻塞其餘事務修改表數據。
  • 行鎖

    • 共享鎖(S):容許事務去讀一行,阻止其餘事務對該數據進行修改,select ... lock in share mode
    • 排它鎖(X):容許事務去讀取更新數據,阻止其餘事務對數據進行查詢或者修改,select ... for update
  • 意向鎖:搭配行鎖,阻塞表鎖;申請S鎖時,先申請IS鎖;申請X鎖,先申請IS鎖,由數據庫自動完成
    • 意向共享鎖(IS):當一個事務要給一條數據加S鎖的時候,會先對數據所在的表先加上IS鎖,成功後才能加上S鎖
    • 意向排它鎖(IX):當一個事務要給一條數據加X鎖的時候,會先對數據所在的表先加上IX鎖,成功後才能加上X鎖

注:意向鎖搭配行鎖使用來阻塞表鎖,如:

  • 會話A申請了S鎖。
  • 會話B但願申請整個表的寫鎖。
  • 可是因爲S鎖會申請IS鎖,因此會話B發現有IS鎖存在則阻塞直到會話A結束。

(8)鎖算法

  • Record Lock(如下簡稱RL):單行鎖定。
  • Gap Lock(如下簡稱GL):範圍鎖定,不包括當前行。
  • Next-Key Lock(如下簡稱NKL):Record+Gap,鎖定一個範圍,包括範圍自己。

2 控制事務處理

  • BEGIN或START TRANSACTION:顯式開啓一個事務
  • COMMIT:提交事務,並已對數據庫進行的全部修改爲爲永久性的
  • ROLLBACK:回滾將結束事務並撤銷正在進行的全部未提交的修改
  • SAVEPOINT identifier:事務中建立保留點
  • RELEASE SAVEPOINT identifier:刪除一個事務的保存點
  • ROLLBACK TO identifier:回滾到一個事務保存點
  • SET TRANSACTION:設置事務的隔離級別。其中innoDB存儲引擎的隔離級別4個。
/*
BEGIN:開啓一個事務
ROLLBACK:發生異常回滾
COMMIT:事務正常提交
*/


/*
SET AUTOCOMMIT=0:禁止自動提交
SET AUTOCOMMIT=1:開啓自動提交
*/

二十一 使用遊標

1 遊標

遊標(cursor)是一個存儲在DBMS服務器上的數據庫查詢,它不是一條SELECT語句,而是被該語句檢索出來的結果集。

特性:

  • 可以標記遊標爲只讀。
  • 能控制能夠執行的定向操做(向前、向後、第1、最後、絕對位置、相對位置等)。
  • 能標記某些列爲可編輯,某些列爲不可編輯。
  • 規定範圍,使遊標對建立它的特定請求(如存儲過程)或對全部請求可訪問。
  • 指示DBMS對檢索出的數據(而不是指出表中活動數據)進行復制,使數據在遊標打開和訪問期間不變化。

2 使用遊標

-- 建立遊標
DECLARE CustCursor CURSOR
FOR
SELECT * FROM Customers
WHERE cust_email IS NULL

-- 使用遊標
DECLARE @cust_id CHAR(10),
    @cust_name CHAR(50),
    @cust_address CHAR(50),
    @cust_city CHAR(50),
    @cust_state CHAR(5),
    @cust_zip CHAR(10),
    @cust_country CHAR(50),
    @cust_contact CHAR(50),
    @cust_email CHAR(255)

-- 開啓遊標
OPEN CustCursor    
-- 獲取下一行
FETCH NEXT FROM CustCursor
INTO @cust_id, @cust_name, @cust_address,
    @cust_city, @cust_state, @cust_zip,
    @cust_country, @cust_contact, @cust_email
WHILE @@FETCH_STATUS = 0    

BEGIN
FETCH NEXT FROM CustCursor
    INTO @cust_id, @cust_name, @cust_address,
        @cust_city, @cust_state, @cust_zip,
        @cust_country, @cust_contact, @cust_email
END
-- 關閉遊標
CLOSE CustCursor

二十二 高級SQL特性

1 約束

(1)主鍵

  • 任意兩行的主鍵值不相同
  • 每行都具備一個主鍵值(不爲NULL)
  • 包含主機鍵值得列不修改或更新。
  • 主鍵值不重用。

(2)外鍵

  • 外鍵爲表中一列,值爲另外一個表的主鍵
  • 外鍵能夠防止意外刪除,不過部分DBMS支持級聯刪除(cascading delete)
create table stu(
sid int UNSIGNED primary key auto_increment,
name varchar(20) not null)
TYPE=InnoDB charset=utf8;

create table sc(
    scid int UNSIGNED primary key auto_increment,
    sid int UNSIGNED not null,
    score varchar(20) default '0',
    index (sid),   --外鍵必須加索引
    FOREIGN KEY (sid) REFERENCES stu(sid) 
        ON DELETE CASCADE -- 級聯刪除
        ON UPDATE CASCADE -- 級聯更新
)TYPE=InnoDB charset=utf8;

(3)惟一約束(UNIQUE)

  • 表中包含多個惟一約束
  • 惟一約束列能夠包含NULL
  • 惟一約束列可修改或更新
  • 惟一約束列的值可重複使用

(4)默認值約束(DEFAULT value)

指定列的默認值

(5)非空約束(NOT NULL)

指定列不爲空

2 索引

(1)概述

索引用來排序數據以加快搜索和排序操做的速度。

能夠在一個或多個列上定義索引,使DBMS保存其內容的一個排過序的列表。

開始建立索引前,應該記住如下內容:

  • 索引改善檢索操做的性能,但下降了數據插入、修改和刪除的性能。
  • 索引數據可能要佔用大量的存儲空間。
  • 並不是全部數據都適合作索引。取值很少的數據(如州)不如具備更多可能值的數據(如姓或名),能經過索引獲得那麼多的好處。
  • 索引用於數據過濾和數據排序。
  • 能夠在索引中定義多個列(例如,州 + 城市)。這樣的索引僅在以州加城市的順序排序時有用。若是想按城市排序,則這種索引沒有用處。

注:索引的效率隨表數據的增長或改變而變化。許多數據庫管理員發現,過去建立的某個理想的索引通過幾個月的數據處理後可能變得再也不理想了。最好按期檢查索引,並根據須要對索引進行調整。

(2)使用

CREATE INDEX prod_name_ind
ON PRODUCTS (prod_name);

(3)索引分類

  • 普通索引index :加速查找
  • 惟一索引
    • 主鍵索引:primary key :加速查找+約束(不爲空且惟一)惟
    • 一索引:unique:加速查找+約束 (惟一)
  • 聯合索引
    • primary key(id,name):聯合主鍵索引
    • unique(id,name):聯合惟一索引
    • index(id,name):聯合普通索引
  • 全文索引fulltext :用於搜索很長一篇文章的時候,效果最好。

(4)索引類型

  • hash類型的索引:查詢單條快,範圍查詢慢
  • btree類型的索引:b+樹,層數越多,數據量指數級增加(咱們就用它,由於innodb默認支持它)

注:指定全文索引時,無需指定索引類型。

3 觸發器

觸發器是特殊的存儲過程,它在特定的數據庫活動發生時自動執行。觸發器能夠與特定表上的INSERT、UPDATE和DELETE操做(或組合)相關聯。

觸發器內的代碼具備如下數據的訪問權:

  • INSERT操做中的全部新數據;
  • UPDATE操做中的全部新數據和舊數據;
  • DELETE操做中刪除的數據。

常見用途:

  • 保證數據一致
  • 基於某個表的變更在其餘表上執行活動
  • 進行額外的驗證並根據須要回退數據
  • 計算 計算列的值或更新時間戳等
-- 模板
DROP TRIGGER IF EXISTS triggerName; 
create trigger triggerName 
after/before insert/update/delete on tableName
for each row 
begin 
 sql語句; 
end; 

-- 例:更新時間戳
DROP TRIGGER IF EXISTS `upd_info`; 
create trigger upd_info 
after insert on StuCost for each row 
begin 
 update StuCostbyHour set HourCost = HourCost + new.Cost 
 where (TimeJD = hour(new.RecordTime) + 1) and date_format(new.RecordTime, '%Y-%m-%d') = date_format(RecordTime, '%Y-%m-%d'); 
end;

4 安全

  • 對數據庫管理功能(建立表、更改或刪除已存在的表等)的訪問;
  • 對特定數據庫或表的訪問;
  • 訪問的類型(只讀、對特定列的訪問等);
  • 僅經過視圖或存儲過程對錶進行訪問;
  • 建立多層次的安全措施,從而容許多種基於登陸的訪問和控制;
  • 限制管理用戶帳號的能力。

二十三 更多

-- 刪除重複行:只有last_name、first_name和sex都相同才視爲相同
CREATE TABLE tmp SELECT last_name, first_name, sex FROM person_tbl GROUP BY last_name, first_name, sex;
DROP TABLE person_tbl;
ALTER TABLE tmp RENAME TO person_tbl;

參考:

相關文章
相關標籤/搜索