MySQL數據庫使用筆記(四):鏈接查詢與事務

本筆記來自bilibili:一天學會 MySQL 數據庫
完整目錄
MySQL數據庫使用筆記(一):終端使用與約束
MySQL數據庫使用筆記(二):範式設計
MySQL數據庫使用筆記(三):查詢練習
MySQL數據庫使用筆記(四):鏈接查詢與事務

MySQL數據庫使用筆記(四):鏈接查詢與事務

SQL的四種鏈接

內連接

inner join或者join

外鏈接

  • 1.左連接left join或者left outer join
  • 2.右連接right join 或者right outer join
  • 3.完全外鏈接full join或者full outer join

測試

首先創建一個databasecreate database testJoin;
創建person 表:id, name, cardId

create table person(
id int,
name varchar(20),
cardId int
);

創建card表:id, name

create table card(
id  int,
name varchar(20)
);

card表中插入數據:

insert into card values(1, '飯卡');
insert into card values(2, '建行卡');
insert into card values(3, '農行卡');
insert into card values(4, '工商卡');
insert into card values(5, '郵政卡');

person表中插入數據:

insert into person values(1, '張三', 1);
insert into person values(2, '李四', 3);
insert into person values(3, '王五', 6);

但是這裏cardId = 6是無法在card表中找到的。這是沒有使用外鍵。

  • 1.使用inner join(內連接)查詢:
select * from person inner join card on person.cardId=card.id;

+------+------+--------+------+--------+
| id   | name | cardId | id   | name   |
+------+------+--------+------+--------+
|    1 | 張三 |      1 |    1 | 飯卡   |
|    2 | 李四 |      3 |    3 | 農行卡 |
+------+------+--------+------+--------+

內聯查詢,其實就是兩張表中的數據,通過某個字段相等,查詢出相關記錄數據
或者使用select * from person join card on person.cardId=card.id;能得到一樣的結果。

  • 2.left join(左外連接)
select * from person left join card on person.cardId=card.id;

+------+------+--------+------+--------+
| id   | name | cardId | id   | name   |
+------+------+--------+------+--------+
|    1 | 張三 |      1 |    1 | 飯卡   |
|    2 | 李四 |      3 |    3 | 農行卡 |
|    3 | 王五 |      6 | NULL | NULL   |
+------+------+--------+------+--------+

左外連接,會把左邊表裏面的所有數據取出來,而右邊表中的數據,如果有相等的,就會顯示出來,如果沒有,就補NULL
使用left outer join

select * from person left outer join card on person.cardId=card.id;

+------+------+--------+------+--------+
| id   | name | cardId | id   | name   |
+------+------+--------+------+--------+
|    1 | 張三 |      1 |    1 | 飯卡   |
|    2 | 李四 |      3 |    3 | 農行卡 |
|    3 | 王五 |      6 | NULL | NULL   |
+------+------+--------+------+--------+
  • 3.right join(右外連接)
select * from person right join card on person.cardId=card.id;

+------+------+--------+------+--------+
| id   | name | cardId | id   | name   |
+------+------+--------+------+--------+
|    1 | 張三 |      1 |    1 | 飯卡   |
| NULL | NULL |   NULL |    2 | 建行卡 |
|    2 | 李四 |      3 |    3 | 農行卡 |
| NULL | NULL |   NULL |    4 | 工商卡 |
| NULL | NULL |   NULL |    5 | 郵政卡 |
+------+------+--------+------+--------+

右外連接,會把右邊表裏面的所有數據取出來,而左邊表中的數據,如果有相等的,就會顯示出來,如果沒有,就補NULL
使用right outer join

select * from person right outer join card on person.cardId=card.id;

+------+------+--------+------+--------+
| id   | name | cardId | id   | name   |
+------+------+--------+------+--------+
|    1 | 張三 |      1 |    1 | 飯卡   |
| NULL | NULL |   NULL |    2 | 建行卡 |
|    2 | 李四 |      3 |    3 | 農行卡 |
| NULL | NULL |   NULL |    4 | 工商卡 |
| NULL | NULL |   NULL |    5 | 郵政卡 |
+------+------+--------+------+--------+
  • 4.full join(全外連接)

mysql不支持full join

在這裏插入圖片描述
可以使用左連接右連接並集來達到全連接的效果:

select * from person left join card on person.cardId=card.id
union
select * from person right join card on person.cardId=card.id;

+------+------+--------+------+--------+
| id   | name | cardId | id   | name   |
+------+------+--------+------+--------+
|    1 | 張三 |      1 |    1 | 飯卡   |
|    2 | 李四 |      3 |    3 | 農行卡 |
|    3 | 王五 |      6 | NULL | NULL   |
| NULL | NULL |   NULL |    2 | 建行卡 |
| NULL | NULL |   NULL |    4 | 工商卡 |
| NULL | NULL |   NULL |    5 | 郵政卡 |
+------+------+--------+------+--------+

MySQL的事務

mysql中,事務其實是一個最小的不可分割的工作單元。事務能夠保證一個業務的完整性。
比如我們的銀行轉賬:
a -> -100

update user set money=money-100 where name= 'a';

b -> +100

update user set money=money+100 where name= 'b';

實際的程序中,如果只有一條語句執行成功了, 而另外一條沒有執行成功,就會出現數據前後不一一致
多條SQL語句,可能會有同時成功的要求,要麼就同時失敗。

MySQL中如何控制事務

  • 1.MySQL是默認開啓事務的(自動提交)
select @@autocommit;

+--------------+
| @@autocommit |
+--------------+
|            1 |
+--------------+

默認事務開啓的作用是什麼?
當我們去執行一個sql語句的時候,效果會立即體現出來,且不能回滾。
新建一個databasecreate database bank;
新建一個table:

create table user(
id int primary key,
name varchar (20),
money int
);

插入數據:insert into user values(1, 'a', 1000);
事務回滾:撤銷sq1語句執行效果

rollback;

但是查看數據,並沒有回滾成功:

select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

設置mysql自動提交爲false

set autocommit=0 ;

再次查看:

select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            0 |
+--------------+

上面的操作,關閉了mysql 的自動提交(commit)
再次插入數據:insert into user values(2, 'b', 1000);,並查看:select * from user;

+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

使用rollback;進行回滾,再查看:select * from user;

+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

剛剛插入的數據已經消失。
輸入commit;,修改爲手動提交,再撤銷,是不可以撤銷的(持久性)。
總結:

  • 自動提交? @@autocommit=1
  • 手動提交? commit;
  • 事務回滾? rollback;

先插入數據:insert into user values(2, 'b', 1000);
如果這個時候轉賬:

update user set money=money-100 where name ='a';
update user set money=money+100 where name= 'b';

查看數據:

 select * from user;
 
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

接下來進行回滾:rollback;
再次查看:

+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

事務給我們提供了一個返回的機會
輸入:set autocommit=1;

select @@autocommit=1;
+----------------+
| @@autocommit=1 |
+----------------+
|              1 |
+----------------+

begin;或者start transaction;都可以幫我們手動開啓一個事務
插入數據並轉賬:

update user set money=money-100 where name ='a';
update user set money=money+100 where name= 'b';

select * from user;

+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

進行回滾rollback;發現沒有用

  • 1.使用begin;手動開啓事務,轉賬,並查詢數據:
begin;
update user set money=money-100 where name ='a';
update user set money=money+100 where name= 'b';

 select * from user;
 
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   800 |
|  2 | b    |  1200 |
+----+------+-------+

進行回滾rollback;發現有用

  • 2.使用start transaction;手動開啓事務,轉賬,並查詢數據:
start transaction;
update user set money=money-100 where name ='a';
update user set money=money+100 where name= 'b';

select * from user;

+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   800 |
|  2 | b    |  1200 |
+----+------+-------+

進行回滾rollback;發現有用

事務開啓之後,一旦commit 提交,就不可以回滾(也就是當前的這個事務在提交的時候就結束了)

事務的四大特徵:

  • A 原子性:事務是最小的單位,不可以在分割。
  • C 一致性:事務要求,同- -事務中的sql語句,必須保證同時成功或者同時失敗。
  • I 隔離性:事務1和事務2之間是具有隔離性的。
  • D 持久性:事務一旦結束(commitrollback), 就不可以返回。

事務開啓:

  • 1.修改默認提交 set autocommit=0;
  • 2.begin;
  • 3.start transaction;

事務手動提交: commit;
事務手動回滾: rollback;

事務的隔離性:

  • 1、read uncommitted; 讀未提交的
  • 2、read committed; 讀已經提交的
  • 3、repeatable read; 可以重複讀
  • 4、serializable; 串行化

1- read uncommitted;讀未提交的
如果有事務a ,和事務b,
a事務對數據進行操作,在操作的過程中,事務沒有被提交,但是b可以看見a操作的結果。
現在插入數據並查看:

insert into user values(3, '小明', 1000);
insert into user values(4, '淘寶店', 1000);

select * from user;

+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
+----+--------+-------+

如何查看數據庫的隔離級別?
mysql 8.0: select @@global.transaction_isolation; (系統級別)和select @@ ransaction_isolation;(會話級別)

mysql 5.x:select @@global.tx_isolation;(我使用的就是這個版本)和select @@tx_isolation;

+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+

如何修改隔離級別?
輸入:set global transaction isolation level read uncommitted;
查看:select @@global.transaction_isolation; 發現已經改變

+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED      |
+-----------------------+

轉賬:小明在淘寶店買鞋子: 800塊錢,
小明–>成都ATM
淘寶店–>廣州AMT

start transaction;
update user set money=money-800 where name= '小明';
update user set money=money+800 where name= '淘寶店';

select * from user;

+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |   200 |
|  4 | 淘寶店 |  1800 |
+----+--------+-------+

給淘寶店打電話,說你去查一下,是不是到賬了,確實到賬了。但是小明可以回滾數據。。。

如果兩個不同的地方,都在進行操作,如果事務a開啓之後,他的數據可以被其他事務讀取到,這樣就會出現(髒讀)
髒讀: 一個事務讀到了另外-個事務沒有提交的數據,就叫做髒讀。實際開發是不允許髒讀出現的。

2-read committed; 讀已經提交的

先查看當前隔離級別:

select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED      |
+-----------------------+

再修改當前隔離級別:set global transaction isolation level read committed;再次查看:

select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-COMMITTED        |
+-----------------------+

小張:銀行的會計

start transaction;
select * from user;

+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
+----+--------+-------+

小王

start transaction;
insert into user values(5,'C' ,100);
commit;

select * from user;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
|  5 | C      |   100 |
+----+--------+-------+

小張上完廁所,抽完煙回來了

select avg (money) from user;
/*這裏我本地測試的是1000,有可能權限哪裏搞錯了*/
+-------------+
| avg (money) |
+-------------+
|   820.0000 |
+-------------+

money 的平均值不是1000, 變少了?
雖然只能讀到另外一個事務提交的數據,但還是會出現問題,就是讀取同一個表的數據,發現前後不一致。
不可重複讀現象: read committed

3-repeatable read;可以重複讀
查看權限:

select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-COMMITTED        |
+-----------------------+

修改當前隔離級別:set global transaction isolation level repeatable read;,再次查看:

select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+

在REPEATABLE-READ隔離級別下又會出現什麼問題?

select * from user;

+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
|  5 | C      |   100 |
+----+--------+-------+

–張全蛋-成都

start transaction;

–王尼瑪-北京

start transaction; 

–張全蛋-成都

insert into user values(6,'d' ,1000);/*插入數據*/
select * from user;/*查看數據*/

+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
|  5 | C      |   100 |
|  6 | d      |  1000 |
+----+--------+-------+

然後再輸入commit;

–王尼瑪-北京

select * from user;/*查看數據*/
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
|  5 | C      |   100 |

插入數據,發現有問題

insert into user values(6,'d' ,1000); 

ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'

這種現象就叫做幻讀!
事務a和事務b同時操作一-張表, 事務a提交的數據,也不能被事務b讀到,就可以造成幻讀

4-serializable; 串行化
查詢隔離等級:select @@global.tx_isolation;

+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+

修改等級:set global transaction isolation level serializable;

+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| SERIALIZABLE          |
+-----------------------+

先查看當前數據:

select * from user;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
|  5 | C      |   100 |
|  6 | d      |  1000 |
+----+--------+-------+

–張全蛋-成都

start transaction;

–王尼瑪-北京

start transaction; 

–張全蛋-成都

insert into user values(7, '趙鐵柱', 1000);
commit;

–王尼瑪-北京
查看數據

select * from user;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | a      |   800 |
|  2 | b      |  1200 |
|  3 | 小明   |  1000 |
|  4 | 淘寶店 |  1000 |
|  5 | C      |   100 |
|  6 | d      |  1000 |
|  7 | 趙鐵柱 |  1000 |
+----+--------+-------+

–張全蛋-成都
下面的sql語句卡住了

start transaction;
insert  into user values(8, '王小花' ,1000);

當user表被另外一個事務操作的時候,其他事務裏面的寫操作,是不可以進行的。
進入排隊狀態(串行化),直到王尼瑪那邊事務結束之後,張全蛋這個的寫入操作纔會執行。
在沒有等待超時的情況下。

–王尼瑪-北京
必須輸入commit;

commit;

張全蛋-成都
在王尼瑪輸入完commit;,這個時候張全蛋顯示:Query OK, 1 row affected (6.11 sec)

  • 串行化問題是,性能特差! ! !

READ-UNCOMMITTED > READ- COMMITTED > REPEATABLE—READ > SERIALIZABLE;

  • 隔離級別越高,性能越差

mysql默認隔離級別是REPEATABLE—READ

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章