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

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