本笔记来自bilibili:一天学会 MySQL 数据库
完整目录:
MySQL数据库使用笔记(一):终端使用与约束
MySQL数据库使用笔记(二):范式设计
MySQL数据库使用笔记(三):查询练习
MySQL数据库使用笔记(四):链接查询与事务
inner join或者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
表中找到的。这是没有使用外键。
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;
能得到一样的结果。
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 |
+------+------+--------+------+--------+
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 | 邮政卡 |
+------+------+--------+------+--------+
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中,事务其实是一个最小的不可分割的工作单元。事务能够保证一个业务的完整性。
比如我们的银行转账:
a -> -100
update user set money=money-100 where name= 'a';
b -> +100
update user set money=money+100 where name= 'b';
实际的程序中,如果只有一条语句执行成功了, 而另外一条没有执行成功,就会出现数据前后不一一致。
多条SQL语句,可能会有同时成功的要求,要么就同时失败。
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;
发现没有用。
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;
发现有用。
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 提交,就不可以回滚(也就是当前的这个事务在提交的时候就结束了)。
commit
, rollback
), 就不可以返回。事务开启:
set autocommit=0;
begin;
start transaction;
事务手动提交: commit;
事务手动回滚: rollback;
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