本筆記來自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
測試
首先創建一個database
:create 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語句的時候,效果會立即體現出來,且不能回滾。
新建一個database
:create 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 持久性:事務一旦結束(
commit
,rollback
), 就不可以返回。
事務開啓:
- 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