1. 程式人生 > 資料庫 >MySQL多表查詢與事務

MySQL多表查詢與事務

學習目標

  1. 能夠使用內連線進行多表查詢
  2. 能夠使用左外連線和右外連線進行多表查詢
  3. 能夠使用子查詢進行多表查詢
  4. 能夠理解多表查詢的規律
  5. 能夠理解事務的概念
  6. 能夠說出事務的原理
  7. 能夠在MySQL中使用事務
  8. 能夠理解髒讀,不可重複讀,幻讀的概念及解決辦法

第1章 多表查詢

1.1 什麼是多表查詢

同時查詢多張表獲取到需要的資料
比如:我們想查詢到開發部有多少人,需要將部門表和員工表同時進行查詢
在這裡插入圖片描述

多表查詢的分類
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-yoz81IHA-1582193672229)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A202.png)]

準備資料:

-- 建立部門表
CREATE TABLE dept (
  id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(20)
);

INSERT INTO dept (NAME) VALUES ('開發部'),('市場部'),('財務部');

-- 建立員工表
CREATE TABLE emp (
  id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(10),gender CHAR(1),-- 性別
  salary DOUBLE,-- 工資
  join_date DATE,-- 入職日期
  dept_id INT
);

INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孫悟空','男',7200,'2013-02-24',1);
INSERT INTO emp(NAME,dept_id) VALUES('豬八戒',3600,'2010-12-02',2);
INSERT INTO emp(NAME,dept_id) VALUES('唐僧',9000,'2008-08-08',dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
INSERT INTO emp(NAME,dept_id) VALUES('蜘蛛精',4500,'2011-03-14',1);

1.2 笛卡爾積現象

1.2.1 什麼是笛卡爾積現象

多表查詢時左表的每條資料和右表的每條資料組合,這種效果成為笛卡爾積

需求:查詢每個部門有哪些人

具體操作:

SELECT * FROM dept,emp;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-6RM1mmMm-1582193672230)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A203.png)]

以上資料其實是左表的每條資料和右表的每條資料組合。左表有3條,右表有5條,最終組合後3*5=15條資料。

左表的每條資料和右表的每條資料組合,這種效果稱為笛卡爾乘積
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-kSxUkYTV-1582193672230)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A204.png)]

1.2.2 如何清除笛卡爾積現象的影響

我們發現不是所有的資料組合都是有用的,只有員工表.dept_id = 部門表.id 的資料才是有用的。所以需要通過條件過濾掉沒用的資料。
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-4hic9Zob-1582193672231)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A205.png)]

SELECT * FROM dept,emp WHERE emp.`dept_id`=dept.`id`; 

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Y6m9Bho3-1582193672231)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A206.png)]

1.3 內連線

用左邊表的記錄去匹配右邊表的記錄,如果符合條件的則顯示

1.3.1 隱式內連線

隱式內連線:看不到JOIN關鍵字,條件使用WHERE指定
SELECT 欄位名 FROM 左表,右表 WHERE 條件;

1.3.2 顯示內連線

顯示內連線:使用INNER JOIN ... ON語句,可以省略INNER
SELECT 欄位名 FROM 左表 INNER JOIN 右表 ON 條件;

具體操作:

  • 查詢唐僧的資訊,顯示員工id,姓名,性別,工資和所在的部門名稱,我們發現需要聯合2張表同時才能查詢出需要的資料,我們使用內連線

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-yhsxVO0N-1582193672231)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A215.png)]

  1. 確定查詢哪些表
SELECT * FROM dept INNER JOIN emp;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-z018Pz8z-1582193672231)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A207.png)]

  1. 確定表連線條件,員工表.dept_id = 部門表.id 的資料才是有效的
SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id`;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-BUN35u5h-1582193672232)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A208.png)]

  1. 確定表連線條件,我們查詢的是唐僧的資訊,部門表.name=‘唐僧’
SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id` AND emp.`NAME`='唐僧';

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-KGF6mpfW-1582193672232)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A209.png)]

  1. 確定查詢欄位,查詢唐僧的資訊,顯示員工id,姓名,性別,工資和所在的部門名稱
SELECT emp.`id`,emp.`NAME`,emp.`gender`,emp.`salary`,dept.`NAME` FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id` AND emp.`NAME`='唐僧';

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-IrOpAC6q-1582193672232)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A210.png)]

  1. 我們發現寫表名有點長,可以給表取別名,顯示的欄位名也使用別名
SELECT e.`id` 員工編號,e.`NAME` 員工姓名,e.`gender` 性別,e.`salary` 工資,d.`NAME` 部門名稱 FROM dept d INNER JOIN emp e ON e.`dept_id`=d.`id` AND e.`NAME`='唐僧';

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-mVfTzdhu-1582193672233)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A211.png)]

總結內連線查詢步驟:

  1. 確定查詢哪些表
  2. 確定表連線條件
  3. 確定查詢欄位

1.4 左外連線

左外連線:使用LEFT OUTER JOIN ... ONOUTER可以省略
SELECT 欄位名 FROM 左表 LEFT OUTER JOIN 右表 ON 條件;
用左邊表的記錄去匹配右邊表的記錄,如果符合條件的則顯示;否則,顯示NULL
可以理解為:在內連線的基礎上保證左表的資料全部顯示

具體操作:

  • 在部門表中增加一個銷售部
INSERT INTO dept (NAME) VALUES ('銷售部');

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-VGc9Bvk0-1582193672233)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A212.png)]

  • 使用內連線查詢
SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id`;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-xh9CIs1O-1582193672233)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A213.png)]

  • 使用左外連線查詢
SELECT * FROM dept LEFT OUTER JOIN emp ON emp.`dept_id`=dept.`id`;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-8juI6jme-1582193672233)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A214.png)]

1.5 右外連線

右外連線:使用RIGHT OUTER JOIN ... ONOUTER可以省略
SELECT 欄位名 FROM 左表 RIGHT OUTER JOIN 右表 ON 條件;
用右邊表的記錄去匹配左邊表的記錄,如果符合條件的則顯示;否則,顯示NULL
可以理解為:在內連線的基礎上保證右表的資料全部顯示

具體操作:

  • 在員工表中增加一個員工
  INSERT INTO emp(NAME,dept_id) VALUES('沙僧',6666,NULL);

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-NOm3W26G-1582193672234)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A217.png)]

  • 使用內連線查詢
 SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id`;

在這裡插入圖片描述

  • 使用右外連線查詢
  SELECT * FROM dept RIGHT OUTER JOIN emp ON emp.`dept_id`=dept.`id`;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-DC6Iau3z-1582193672234)(imgs/%E8%A1%A8%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A216.png)]

1.6 子查詢

一條SELECT語句結果作為另一條SELECT語法一部分(查詢條件,查詢結果,表)
SELECT 查詢欄位 FROM 表 WHERE 查詢條件;
SELECT * FROM employee WHERE salary=(SELECT MAX(salary) FROM employee);
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-IRVjnMLw-1582193672234)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A201.png)]
子查詢需要放在()中

子查詢結果的三種情況:

  1. 子查詢的結果是一個值的時候
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-PMcJU59G-1582193672234)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A202.png)]
  2. 子查詢結果是單例多行的時候
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-f1MHyqYo-1582193672235)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A203.png)]
  3. 子查詢的結果是多行多列
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-YjNgn244-1582193672235)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A204.png)]

說明:
子查詢結果只要是單列,肯定在WHERE後面作為條件
子查詢結果只要是多列,肯定在FROM後面作為

1.6.1 子查詢的結果是一個值的時候

子查詢結果只要是單列,肯定在WHERE後面作為條件
SELECT 查詢欄位 FROM 表 WHERE 欄位=(子查詢);

  1. 查詢工資最高的員工是誰?

    1. 查詢最高工資是多少
      SELECT MAX(salary) FROM emp;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-ODFYReQe-1582193672235)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A205.png)]

    1. 根據最高工資到員工表查詢到對應的員工資訊
      SELECT * FROM emp WHERE salary=(SELECT MAX(salary) FROM emp);
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-xnUYZSL8-1582193672235)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A206.png)]

  2. 查詢工資小於平均工資的員工有哪些?

    1. 查詢平均工資是多少
      SELECT AVG(salary) FROM emp;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-tZG7PU60-1582193672235)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A207.png)]

    1. 到員工表查詢小於平均的員工資訊
      SELECT * FROM emp WHERE salary < (SELECT AVG(salary) FROM emp);
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-NGspJfO3-1582193672236)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A208.png)]

1.6.2 子查詢結果是單例多行的時候

子查詢結果只要是單列,肯定在WHERE後面作為條件
子查詢結果是單例多行,結果集類似於一個數組,父查詢使用IN運算子
SELECT 查詢欄位 FROM 表 WHERE 欄位 IN (子查詢);

  1. 查詢工資大於5000的員工,來自於哪些部門的名字

    1. 先查詢大於5000的員工所在的部門id
      SELECT dept_id FROM emp WHERE salary > 5000;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-BcFv0C7O-1582193672236)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A209.png)]

    1. 再查詢在這些部門id中部門的名字
      SELECT dept.name FROM dept WHERE dept.id IN (SELECT dept_id FROM emp WHERE salary > 5000);
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-rJcOYOte-1582193672236)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A210.png)]

  2. 查詢開發部與財務部所有的員工資訊

    1. 先查詢開發部與財務部的id
    SELECT id FROM dept WHERE NAME IN('開發部','財務部');
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-JCX9kVoM-1582193672236)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A211.png)]

    1. 再查詢在這些部門id中有哪些員工
    SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME IN('開發部','財務部'));
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Y7pnk7b4-1582193672236)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A212.png)]

1.6.3 子查詢的結果是多行多列

子查詢結果只要是多列,肯定在FROM後面作為
SELECT 查詢欄位 FROM (子查詢) 表別名 WHERE 條件;
子查詢作為表需要取別名,否則這張表沒用名稱無法訪問表中的欄位

  • 查詢出2011年以後入職的員工資訊,包括部門名稱

    1. 在員工表中查詢2011-1-1以後入職的員工
    SELECT * FROM emp WHERE join_date > '2011-1-1';
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-0JUXKMBr-1582193672237)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A214.png)]

    1. 查詢所有的部門資訊,與上面的虛擬表中的資訊組合,找出所有部門id等於的dept_id
    SELECT * FROM dept d,(SELECT * FROM emp WHERE join_date > '2011-1-1') e WHERE e.dept_id = d.id;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-ImkDe9S6-1582193672237)(imgs/%E5%AD%90%E6%9F%A5%E8%AF%A213.png)]

使用表連線:

SELECT d.*,e.* FROM dept d INNER JOIN emp e ON d.id = e.dept_id WHERE e.join_date > '2011-1-1';

1.6.4 多表查詢總結

  • 子查詢結果只要是單列,肯定在WHERE後面作為條件
    SELECT 查詢欄位 FROM 表 WHERE 欄位=(子查詢);
  • 子查詢結果只要是多列,肯定在FROM後面作為
    SELECT 查詢欄位 FROM (子查詢) 表別名 WHERE 條件;

第2章 多表查詢案例

我們在公司開發中,根據不同的業務需求往往需要通過2張及以上的表中去查詢需要的資料。所以我們有必要學習2張及以上的表的查詢。其實不管是幾張表的查詢,都是有規律可循的。

2.1 準備資料

-- 部門表
CREATE TABLE dept (
  id INT PRIMARY KEY PRIMARY KEY,-- 部門id
  dname VARCHAR(50),-- 部門名稱
  loc VARCHAR(50) -- 部門位置
);

-- 新增4個部門
INSERT INTO dept(id,dname,loc) VALUES 
(10,'教研部','北京'),(20,'學工部','上海'),(30,'銷售部','廣州'),(40,'財務部','深圳');

-- 職務表,職務名稱,職務描述
CREATE TABLE job (
  id INT PRIMARY KEY,jname VARCHAR(20),description VARCHAR(50)
);

-- 新增4個職務
INSERT INTO job (id,jname,description) VALUES
(1,'董事長','管理整個公司,接單'),(2,'經理','管理部門員工'),(3,'銷售員','向客人推銷產品'),(4,'文員','使用辦公軟體');

-- 員工表
CREATE TABLE emp (
  id INT PRIMARY KEY,-- 員工id
  ename VARCHAR(50),-- 員工姓名
  job_id INT,-- 職務id
  mgr INT,-- 上級領導
  joindate DATE,-- 入職日期
  salary DECIMAL(7,2),-- 工資
  bonus DECIMAL(7,-- 獎金
  dept_id INT,-- 所在部門編號
  CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
);

-- 新增員工
INSERT INTO emp(id,ename,job_id,mgr,joindate,bonus,dept_id) VALUES 
(1001,'孫悟空',4,1004,'2000-12-17','8000.00',NULL,20),(1002,'盧俊義',3,1006,'2001-02-20','16000.00','3000.00',30),(1003,'林沖','2001-02-22','12500.00','5000.00',(1004,'唐僧',2,1009,'2001-04-02','29750.00',(1005,'李逵','2001-09-28','14000.00',(1006,'宋江','2001-05-01','28500.00',(1007,'劉備','2001-09-01','24500.00',10),(1008,'豬八戒','2007-04-19','30000.00',(1009,'羅貫中',1,'2001-11-17','50000.00',(1010,'吳用','2001-09-08','15000.00','0.00',(1011,'沙僧','2007-05-23','11000.00',(1012,'2001-12-03','9500.00',(1013,'小白龍',(1014,'關羽',1007,'2002-01-23','13000.00',10);

-- 工資等級表
CREATE TABLE salarygrade (
  grade INT PRIMARY KEY,losalary INT,hisalary INT
);

-- 新增5個工資等級
INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
(1,7000,12000),12010,14000),14010,20000),20010,30000),(5,30010,99990);

分析4張表的關係:通過4張表可以查出一個員工的所有資訊
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-cnznWCpV-1582193672237)(imgs\多表查詢01.png)]

2.2 練習

2.2.1 練習1

查詢所有員工資訊。顯示員工編號,員工姓名,工資,職務名稱,職務描述

具體操作:
1.確定要查詢哪些表:emp e,job j

SELECT * FROM emp e INNER JOIN job j;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-fhG7u7IR-1582193672238)(imgs\多表查詢02.png)]

2.確定表連線條件: e.job_id=j.id

SELECT * FROM emp e INNER JOIN job j ON e.job_id=j.id;

在這裡插入圖片描述

3.確定查詢欄位:員工編號,員工姓名,工資,職務名稱,職務描述

SELECT e.`id`,e.`ename`,e.`salary`,j.`jname`,j.`description` FROM emp e INNER JOIN job j ON e.job_id=j.id;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-WxhDi3Dd-1582193672239)(imgs\多表查詢05.png)]

2.2.2 練習2

查詢所有員工資訊。顯示員工編號,員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置

具體操作:
1. 確定要查詢哪些表,emp e,job j,dept d

SELECT * FROM emp e INNER JOIN job j INNER JOIN dept d;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-9fGi5nAl-1582193672240)(imgs\多表查詢06.png)]

在這裡插入圖片描述

  2. 確定表連線條件 e.job_id=j.id and e.dept_id=d.id
SELECT * FROM emp e INNER JOIN job j INNER JOIN dept d ON e.job_id=j.id AND e.dept_id=d.id;

在這裡插入圖片描述

  3. 確定查詢欄位:員工編號,員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置
SELECT e.`id`,j.`description`,d.`dname`,d.`loc` FROM emp e INNER JOIN job j INNER JOIN dept d ON e.job_id=j.id AND e.dept_id=d.id;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-q3ymjWvZ-1582193672241)(imgs\多表查詢10.png)]

2.2.3 練習3

查詢所有員工資訊。顯示員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級

具體操作:
1. 確定要查詢哪些表,emp e,dept d,salarygrade s

SELECT * FROM emp e INNER JOIN job j INNER JOIN dept d INNER JOIN salarygrade s;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-RQloeO6c-1582193672241)(imgs\多表查詢11.png)]
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-urUpTuDO-1582193672242)(imgs\多表查詢12.png)]

  2. 確定表連線條件 e.job_id=j.id and e.dept_id=d.id and e.salary between s.losalary and hisalary
SELECT * FROM emp e INNER JOIN job j INNER JOIN dept d INNER JOIN salarygrade s ON e.job_id=j.id AND e.dept_id=d.id AND e.salary BETWEEN s.losalary AND hisalary;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-8qwigFz2-1582193672242)(imgs\多表查詢13.png)]

  3. 確定查詢欄位:員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級
SELECT e.`ename`,d.`loc`,s.`grade` FROM emp e INNER JOIN job j INNER JOIN dept d INNER JOIN salarygrade s ON e.job_id=j.id AND e.dept_id=d.id AND e.salary BETWEEN s.losalary AND hisalary;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-DDFDTrsb-1582193672242)(imgs\多表查詢15.png)]

2.2.3.1 多表查詢規律總結

  1. 不管我們查詢幾張表,表連線查詢會產出笛卡爾積,我們需要消除笛卡爾積,拿到正確的資料。我們需要找到表與表之間通過哪個欄位關聯起來的(通常是外來鍵=主鍵
  2. 消除笛卡爾積規律:2張表需要1個條件,3張表需要2個條件,4張表需要3個條件。(條件數量=表的數量-1),每張表都要參與進來
  3. 多表連線查詢步驟:
    3.1. 確定要查詢哪些表
    3.2. 確定表連線條件
    3.3. 確定查詢欄位

2.2.4 練習4

查詢經理的資訊。顯示員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級

具體操作:
1. 確定要查詢哪些表,emp e,salarygrade s

  SELECT * FROM emp e INNER JOIN job j INNER JOIN dept d INNER JOIN salarygrade s;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-jJa0FiFv-1582193672242)(imgs\多表查詢11.png)]

  2. 確定表連線條件 e.job_id=j.id and e.dept_id=d.id and e.salary between s.losalary and hisalary
SELECT * FROM emp e INNER JOIN job j INNER JOIN dept d INNER JOIN salarygrade s ON e.job_id=j.id AND e.dept_id=d.id AND e.salary BETWEEN s.losalary AND hisalary;

在這裡插入圖片描述

額外條件:只需要查詢經理的資訊(j.jname=‘經理’)
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-CTr22ZIc-1582193672243)(imgs\多表查詢16.png)]

  3. 確定查詢欄位:員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級
SELECT e.`ename`,s.`grade` FROM emp e INNER JOIN job j INNER JOIN dept d INNER JOIN salarygrade s ON e.job_id=j.id AND e.dept_id=d.id AND e.salary BETWEEN s.losalary AND hisalary AND j.jname='經理';

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-pSTGqejF-1582193672243)(imgs\多表查詢17.png)]

2.2.5 練習5

查詢出部門編號、部門名稱、部門位置、部門人數

具體操作:
1. 去員工表中找到每個部門的人數和部門id

SELECT dept_id,COUNT(*) FROM emp GROUP BY dept_id;

在這裡插入圖片描述

  2. 再和部門表連線查詢
SELECT * FROM dept d INNER JOIN (SELECT dept_id,COUNT(*) FROM emp GROUP BY dept_id) e ON e.dept_id=d.`id`;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-C0lD7HfO-1582193672244)(imgs\多表查詢19.png)][外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-mGPJ1F4D-1582193672244)(imgs\多表查詢20.png)]

  3. 顯示對應的欄位
SELECT d.`id`,d.dname,e.total 部門人數 FROM dept d INNER JOIN (SELECT dept_id,COUNT(*) total FROM emp GROUP BY dept_id) e ON e.dept_id=d.`id`;

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-uDhbTpgb-1582193672244)(imgs\多表查詢21.png)]
最終效果:
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-SYJoUtLy-1582193672244)(imgs\多表查詢22.png)]

第3章 事務安全

3.1 事務的應用場景說明

在實際的業務開發中,有些業務操作要多次訪問資料庫。一個業務要傳送多條SQL語句給資料庫執行。需要將多次訪問資料庫的操作視為一個整體來執行,要麼所有的SQL語句全部執行成功。如果其中有一條SQL語句失敗,就進行事務的回滾,所有的SQL語句全部執行失敗。

例如: 張三給李四轉賬,張三賬號減錢,李四賬號加錢

-- 建立資料表
CREATE TABLE account (
	id INT PRIMARY KEY AUTO_INCREMENT,balance DOUBLE
);

-- 新增資料
INSERT INTO account (NAME,balance) VALUES ('張三',1000),('李四',1000);

模擬張三給李四轉500元錢,一個轉賬的業務操作最少要執行下面的2條語句:

  1. 張三賬號-500
  2. 李四賬號+500
-- 1. 張三賬號-500
UPDATE account SET balance = balance - 500 WHERE id=1;
-- 2. 李四賬號+500
UPDATE account SET balance = balance + 500 WHERE id=2;
假設當張三賬號上-500元,伺服器崩潰了。李四的賬號並沒有+500元,資料就出現問題了。我們需要保證其中一條SQL語句出現問題,整個轉賬就算失敗。只有兩條SQL都成功了轉賬才算成功。這個時候就需要用到事務

3.2 操作事務

MYSQL中可以有兩種方式進行事務的操作:1.手動提交事務2.自動提交事務

3.2.1 手動提交事務

事務有關的SQL語句:

SQL語句 描述
start transaction; 開啟事務
commit; 提交事務
rollback; 回滾事務

手動提交事務使用步驟
第1種情況:開啟事務 -> 執行SQL語句 -> 成功 -> 提交事務
第2種情況:開啟事務 -> 執行SQL語句 -> 失敗 -> 回滾事務
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Axpv6MUM-1582193672244)(imgs\事務01.png)]

案例演示1:模擬張三給李四轉500元錢(成功)
目前資料庫資料如下:
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-OkJN2bXc-1582193672245)(imgs\事務03.png)]

  1. 使用DOS控制檯進入MySQL

  2. 執行以下SQL語句: 1.開啟事務2.張三賬號-5003.李四賬號+500

    START TRANSACTION;
    UPDATE account SET balance = balance - 500 WHERE id=1;
    UPDATE account SET balance = balance + 500 WHERE id=2;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-OsgzCIRB-1582193672245)(imgs\事務02.png)]

  3. 使用SQLYog檢視資料庫:發現數據並沒有改變
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-yMMBQSgX-1582193672245)(imgs\事務03.png)]

  4. 在控制檯執行commit提交任務:
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-DosJHbSS-1582193672245)(imgs\事務04.png)]

  5. 使用SQLYog檢視資料庫:發現數據改變
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-aPF4f9Qb-1582193672245)(imgs\事務05.png)]


案例演示2:模擬張三給李四轉500元錢(失敗)
目前資料庫資料如下:
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-gOFG6SlI-1582193672245)(imgs\事務05.png)]

  1. 在控制檯執行以下SQL語句:1.開啟事務2.張三賬號-500

    START TRANSACTION;
    UPDATE account SET balance = balance - 500 WHERE id=1;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Vuv9q9Oc-1582193672246)(imgs\事務06.png)]

  2. 使用SQLYog檢視資料庫:發現數據並沒有改變
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-atwTHN7a-1582193672246)(imgs\事務07.png)]

  3. 在控制檯執行rollback回滾事務:
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-p4w5ktAB-1582193672246)(imgs\事務08.png)]

  4. 使用SQLYog檢視資料庫:發現數據沒有改變
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-BBux9wla-1582193672246)(imgs\事務09.png)]

總結:
如果事務中SQL語句沒有問題,commit提交事務,會對資料庫資料的資料進行改變。
如果事務中SQL語句有問題,rollback回滾事務,會回退到開啟事務時的狀態。

3.2.2 自動提交事務

MySQL的每一條DML(增刪改)語句都是一個單獨的事務,每條語句都會自動開啟一個事務,執行完畢自動提交事務,MySQL預設開始自動提交事務

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-7jPVlpKK-1582193672246)(imgs\事務10.png)]

  1. 將金額重置為1000
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-tVnfQEgJ-1582193672246)(imgs\事務11.png)]

  2. 執行以下SQL語句

    UPDATE account SET balance = balance - 500 WHERE id=1;
    
  3. 使用SQLYog檢視資料庫:發現數據已經改變
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-CzzoL8VY-1582193672247)(imgs\事務12.png)]

    通過修改MySQL全域性變數"autocommit",取消自動提交事務
    使用SQL語句:show variables like '%commit%';檢視MySQL是否開啟自動提交事務
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-MMZT2kCK-1582193672247)(imgs\事務13.png)]
    0:OFF(關閉自動提交)
    1:ON(開啟自動提交)

  4. 取消自動提交事務,設定自動提交的引數為OFF,執行SQL語句:set autocommit = 0;
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-wMvfjwnn-1582193672247)(imgs\事務14.png)]

  5. 在控制檯執行以下SQL語句:張三-500

    UPDATE account SET balance = balance - 500 WHERE id=1;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-NwFxXYlf-1582193672247)(imgs\事務15.png)]

  6. 使用SQLYog檢視資料庫,發現數據並沒有改變
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-iy0WtGqI-1582193672247)(imgs\事務16.png)]

  7. 在控制檯執行commit提交任務
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-WeYCPWcF-1582193672247)(imgs\事務17.png)]

  8. 使用SQLYog檢視資料庫,發現數據改變
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-1mMUSwKG-1582193672248)(imgs\事務18.png)]

3.3 事務原理

事務開啟之後,所有的操作都會臨時儲存到事務日誌,事務日誌只有在得到`commit`命令才會同步到資料表中,其他任何情況都會清空事務日誌(rollback,斷開連線)

在這裡插入圖片描述

3.4 回滾點

在某些成功的操作完成之後,後續的操作有可能成功有可能失敗,但是不管成功還是失敗,前面操作都已經成功,可以在當前成功的位置設定一個回滾點。可以供後續失敗操作返回到該位置,而不是返回所有操作,這個點稱之為回滾點。

設定回滾點語法:savepoint 回滾點名字;
回到回滾點語法: rollback to 回滾點名字;

具體操作:

  1. 將資料還原到1000
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-HZ9zPcLg-1582193672248)(imgs\事務20.png)]

  2. 開啟事務
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-8guQ4kTu-1582193672248)(imgs\事務21.png)]

  3. 讓張三賬號減3次錢

    UPDATE account SET balance = balance - 10 WHERE id=1;
    UPDATE account SET balance = balance - 10 WHERE id=1;
    UPDATE account SET balance = balance - 10 WHERE id=1;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-n6VQnjWr-1582193672248)(imgs\事務22.png)]

  4. 設定回滾點:savepoint abc;
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-VsZqq9SC-1582193672249)(imgs\事務23.png)]

  5. 讓張三賬號減4次錢

    UPDATE account SET balance = balance - 10 WHERE id=1;
    UPDATE account SET balance = balance - 10 WHERE id=1;
    UPDATE account SET balance = balance - 10 WHERE id=1;
    UPDATE account SET balance = balance - 10 WHERE id=1;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-pJxTN4vm-1582193672249)(imgs\事務24.png)]

  6. 回到回滾點:rollback to abc;
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-VVWL5exw-1582193672249)(imgs\事務25.png)]

  7. 分析過程
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-RLuTYbMF-1582193672249)(imgs\事務26.png)]

  8. 如果是事務回滾到指定的位置上,如果需要生效,那麼必須要commit提交。

總結:設定回滾點可以讓我們在失敗的時候回到回滾點,而不是回到事務開啟的時候。

3.5 事務的四大特性

3.5.1 事務的四大特性

事務特性 含義
原子性(Atomicity) 事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。
一致性(Consistency) 事務前後資料的完整性必須保持一致
隔離性(Isolation) 是指多個使用者併發訪問資料庫時,一個使用者的事務不能被其它使用者的事務所幹擾,多個併發事務之間資料要相互隔離,不能相互影響。
永續性(Durability) 指一個事務一旦被提交,它對資料庫中資料的改變就是永久性的,接下來即使資料庫發生故障也不應該對其有任何影響

3.5.2 事務的隔離級別

事務在操作時的理想狀態:多個事務之間互不影響,如果隔離級別設定不當就可能引發併發訪問問題。

併發訪問的問題 含義
髒讀 一個事務讀取到了另一個事務中尚未提交的資料
不可重複讀 一個事務中兩次讀取的資料內容不一致,要求的是一個事務中多次讀取時資料是一致的,這是事務update時引發的問題
幻讀 一個事務中兩次讀取的資料的數量不一致,要求在一個事務多次讀取的資料的數量是一致的,這是insert或delete時引發的問題

[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-v5QtROIs-1582193672249)(imgs\事務58.png)]

MySQL資料庫有四種隔離級別:上面的級別最低,下面的級別最高。“是”表示會出現這種問題,“否”表示不會出現這種問題。

級別 名字 隔離級別 髒讀 不可重複讀 幻讀 資料庫預設隔離級別
1 讀未提交 read uncommitted
2 讀已提交 read committed Oracle和SQL Server
3 可重複讀 repeatable read MySQL
4 序列化 serializable

MySQL事務隔離級別相關的命令

  1. 查詢全域性事務隔離級別

    show variables like '%isolation%';
    -- 或
    select @@tx_isolation;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-kGaeNF17-1582193672250)(imgs\事務27.png)]

  2. 設定事務隔離級別,需要退出MSQL再進入MYSQL才能看到隔離級別的變化

    set global transaction isolation level 級別字串;
    -- 如:
    set global transaction isolation level read uncommitted;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-KtFkghZ2-1582193672250)(imgs\事務28.png)]

3.5.2.1 髒讀的演示

將資料進行恢復:UPDATE account SET balance = 1000;

  1. 開啟A視窗登入MySQL,設定全域性的隔離級別為最低

    mysql -uroot -proot
    set global transaction isolation level read uncommitted;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-ep7VIJse-1582193672250)(imgs\事務29.png)]

  2. 開啟B視窗,AB視窗都開啟事務

    use day23;
    start transaction;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-HnkxbzoD-1582193672251)(imgs\事務30.png)]

  3. A視窗更新2個人的賬戶資料,未提交

    update account set balance=balance-500 where id=1;
    update account set balance=balance+500 where id=2;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-JzZ7nifH-1582193672251)(imgs\事務31.png)]

  4. B視窗查詢賬戶

    select * from account;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-zYzI6Td7-1582193672251)(imgs\事務32.png)]

  5. A視窗回滾

    rollback;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-EOb3OYHg-1582193672251)(imgs\事務33.png)]

  6. B視窗查詢賬戶,錢沒了
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-VXEojmRb-1582193672251)(imgs\事務34.png)]

髒讀非常危險的,比如張三向李四購買商品,張三開啟事務,向李四賬號轉入500塊,然後打電話給李四說錢已經轉了。李四一查詢錢到賬了,發貨給張三。張三收到貨後回滾事務,李四的再檢視錢沒了。

解決髒讀的問題:將全域性的隔離級別進行提升
將資料進行恢復:UPDATE account SET balance = 1000;

  1. 在A視窗設定全域性的隔離級別為read committed

    set global transaction isolation level read committed;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-RXjbE1Va-1582193672251)(imgs\事務35.png)]

  2. B視窗退出MySQL,B視窗再進入MySQL
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-hdUQmClA-1582193672252)(imgs\事務36.png)]

  3. AB視窗同時開啟事務
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-YnvDCAvW-1582193672252)(imgs\事務37.png)]

  4. A更新2個人的賬戶,未提交

    update account set balance=balance-500 where id=1;
    update account set balance=balance+500 where id=2;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-AF9ERdGP-1582193672252)(imgs\事務38.png)]

  5. B視窗查詢賬戶
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-XICNZwpT-1582193672252)(imgs\事務39.png)]

  6. A視窗commit提交事務
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-S6kqj66g-1582193672252)(imgs\事務40.png)]

  7. B視窗檢視賬戶
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Iz0jHt4x-1582193672255)(imgs\事務41.png)]

結論:read committed的方式可以避免髒讀的發生

3.5.2.2 不可重複讀的演示

將資料進行恢復:UPDATE account SET balance = 1000;

  1. 開啟A視窗

    set global transaction isolation level read committed;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-2fBuxXZM-1582193672255)(imgs\事務42.png)]

  2. 開啟B視窗,在B視窗開啟事務

    start transaction;
    select * from account;
    

在這裡插入圖片描述

  1. 在A視窗開啟事務,並更新資料

    start transaction;
    update account set balance=balance+500 where id=1;
    commit;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Lea8NVuT-1582193672255)(imgs\事務44.png)]

  2. B視窗查詢

    select * from account;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-lS237JcQ-1582193672256)(imgs\事務45.png)]

兩次查詢輸出的結果不同,到底哪次是對的?不知道以哪次為準。
很多人認為這種情況就對了,無須困惑,當然是後面的為準。我們可以考慮這樣一種情況,比如銀行程式需要將查詢結果分別輸出到電腦螢幕和發簡訊給客戶,結果在一個事務中針對不同的輸出目的地進行的兩次查詢不一致,導致檔案和螢幕中的結果不一致,銀行工作人員就不知道以哪個為準了。

解決不可重複讀的問題:將全域性的隔離級別進行提升為:repeatable read
將資料進行恢復:UPDATE account SET balance = 1000;

  1. A視窗設定隔離級別為:repeatable read

    set global transaction isolation level repeatable read;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-mB0OzmYk-1582193672256)(imgs\事務46.png)]

  2. B視窗退出MySQL,B視窗再進入MySQL

    start transaction;
    select * from account;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-XGnqvRbm-1582193672256)(imgs\事務47.png)]

  3. A視窗更新資料

    start transaction;
    update account set balance=balance+500 where id=1;
    commit;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-8E96CTTp-1582193672256)(imgs\事務48.png)]

結論:同一個事務中為了保證多次查詢資料一致,必須使用repeatable read隔離級別
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-5Y5QYoXo-1582193672256)(imgs\事務50.png)]

3.5.2.3 幻讀的演示

在MySQL中無法看到幻讀的效果。但我們可以將事務隔離級別設定到最高,以擋住幻讀的發生
將資料進行恢復:UPDATE account SET balance = 1000;

  1. 開啟A視窗

    set global transaction isolation level serializable; -- 設定隔離級別為最高
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-W0CL7P9k-1582193672257)(imgs\事務51.png)]

  2. A視窗退出MySQL,A視窗重新登入MySQL

    start transaction;
    select count(*) from account;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-CnyLgGZM-1582193672257)(imgs\事務52.png)]

  3. 再開啟B視窗,登入MySQL

  4. 在B視窗中開啟事務,新增一條記錄

    start transaction; -- 開啟事務
    insert into account (name,balance) values ('LaoWang',500);
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-n0eyriwz-1582193672257)(imgs\事務53.png)]

  5. 在A視窗中commit提交事務,B視窗中insert語句會在A視窗事務提交後立馬執行
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-jf4VGjmA-1582193672257)(imgs\事務54.png)]

  6. 在A視窗中接著查詢,發現數據不變

    select count(*) from account;
    

    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-xbwYBYgg-1582193672257)(imgs\事務55.png)]

  7. B視窗中commit提交當前事務
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Xh46LW6e-1582193672258)(imgs\事務56.png)]

  8. A視窗就能看到最新的資料
    [外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-OApxovCl-1582193672258)(imgs\事務57.png)]

結論:使用serializable隔離級別,一個事務沒有執行完,其他事務的SQL執行不了,可以擋住幻讀