欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

alin的学习之路(数据库篇:三)(多表查询,子查询,集合运算,数据处理)

程序员文章站 2022-05-07 08:02:06
...

alin的学习之路(数据库篇:三)(多表查询,子查询,集合运算,数据处理)

1. 多表查询

1.1 笛卡儿积

笛卡尔积就是两个集合的乘积计算 。
如果多个表进行联合查询, 得到结果是一个笛卡尔积, 举例: 比如有两个表 aaa , bbb
行数: 两个表中行数的乘积
列数: 两个表中列数之和

alin的学习之路(数据库篇:三)(多表查询,子查询,集合运算,数据处理)

1.2 等值连接/不等值连接

等值连接和不等值连接的作用: 通过判断过滤多表查询得到的笛卡尔积中的无效数据

等值连接和不等值连接的区别:where后面判断条件使用的符号是 = 还是其他符号

  • 等值连接

查询员工信息:员工号,姓名,月薪(在emp表中)和部门名称(在dept表中)

-- from后边可以加多张表, 表名之间使用 ,(逗号) 间隔
-- select 后边的列名, 一般不是直接指定, 而是: 表名.列名

SQL> select e.empno, e.ename, e.sal, d.dname from emp e, dept d where e.deptno=d.deptno;

EMPNO ENAME                                                    SAL DNAME
----- -------------------------------------------------- --------- ------------------------------
 7840 岸本_齐史                                            5000.00 周刊少年Jump
 7839 尾田_荣一郎                                          5000.00 周刊少年Jump
 7521 娜美                                                 4560.00 草帽海贼团
 7566 蒙奇·D·路飞                                         5450.00 草帽海贼团
 7654 妮可·罗宾                                           5500.00 草帽海贼团
 7788 乌索普                                               3000.00 草帽海贼团
 7708 *                                                3000.00 草帽海贼团
 
 -- 其中emp后面的e,以及dept后面的d,表示表的别名。可以用来修饰列名。
 -- 也可以不用别名直接用表名,会显得冗杂
  • 不等值连接
  1. 询员工信息:员工号, 姓名, 月薪 和 月薪级别(salgrade表)
-- 员工号,姓名,月薪在 emp 表中
-- 月薪级别在 salgrade 表中

SQL> select e.empno, e.ename, e.sal from emp e, salgrade s where e.sal between s.losal and s.hisal ;

EMPNO ENAME                                                    SAL
----- -------------------------------------------------- ---------
 7934 托尼托尼·乔巴                                         800.68
 7876 布鲁克                                               1100.00
 7844 弗兰奇                                               1500.00
 7707 Franky                                               2000.00
 7900 甚平                                                 2000.00
 7701 Roronoa Zoro                                         2000.00
 7699 夏奇                                                 2050.00
 7782 贝波                                                 2450.00
 7703 Usopp                                                2500.00
  1. 按部门统计员工人数,显示如下信息:部门号,部门名称,人数
-- 部门号和部门名称在dept表中
-- 人数的统计需要在 emp 表中完成

SQL> select d.deptno, d.dname ,count(e.ename) from dept d, emp e where d.deptno=e.deptno 
group by d.deptno, d.dname ;

DEPTNO DNAME                          COUNT(E.ENAME)
------ ------------------------------ --------------
    10 周刊少年Jump                                2
    40 红发海贼团                                  5
    60 D之一族                                     8
    50 黑胡子海贼团                                 4
    20 草帽海贼团                                 19
    30 红心海贼团                                  7

6 rows selected

上述结果中是没有将 45 号部门统计到查询的结果集中的, 应该如何也将其统计到结果集中?

因为45号部门在emp表中没有该部门的成员,所以45号部门并未统计进入该查询。如果想要显示45号部门,需要使用外连接。

1.3 外连接

使用select, 在最后的查询结果中,如果想要包含某些对于where条件来说不成立的记录 , 我们可以使用外
连接. 外连接分为 左外连接 和 右外连接 .
在左连接和右连接时都会以一张A表为基础表,该表的内容会全部显示,然后加上A表和B表匹配的内
容。 如果A表的数据在B表中没有记录。 那么在相关联的结果集行中列显示为空值(NULL)。
对于外连接, 可以使用“(+) ”来表示。 关于使用(+)的一些注意事项:

  1. (+)操作符只能出现在WHERE子句中。
  2. 当使用(+)操作符执行外连接时,如果在WHERE子句中包含有多个条件,则必须在所有条件
    中都包含(+)操作符。
  3. (+)操作符只适用于列,而不能用在表达式上。
  4. (+)操作符不能与 OR 和 IN 操作符一起使用。
  5. (+)操作符只能用于实现左外连接和右外连接,而不能用于实现完全外连接。
    通过 (+) 就可以对查询的结果进行补充, 补充不满足条件的那一方
    举例: where e.deptno = d.deptno
    在d表中 有45 这个部门, 也就是 d.deptno 有可能 等于45
    在表 e中没有45号部门的员工, 因此 e.deptno 绝对不会等于45
    使用 (+) 就可以对不满足条件的结果进行补充, 根据上述的描述 e.deptno 是需要补充的,
    因此将 (+) 写到 e.deptno 后边就可以
    where e.deptno(+) = d.deptno , 要补充的是数据来自于等号右侧的 d.deptno
    这样当 d.deptno=45 的时候 where e.deptno = d.deptno 这个条件成立
  • 左外连接(左边的表不加限制)

用(+)来实现, 这个+号可以这样来理解: + 表示补充,即哪个表有加号,这个表就是匹配
表。如果加号写在右表,左表就是全部显示,所以是左连接。

通俗理解:哪个表少数据了,就在哪个表的那一端加上(+)

-- 再回到刚才的问题,如果想要在查询中也显示出45号部门的员工人数,则需要使用外连接

SQL> select d.deptno, d.dname ,count(e.ename) from dept d, emp e where d.deptno=e.deptno(+) 
	group by d.deptno, d.dname ;

DEPTNO DNAME                          COUNT(E.ENAME)
------ ------------------------------ --------------
    10 周刊少年Jump                                2
    40 红发海贼团                                  5
    45 黑桃海贼团                                  0
    60 D之一族                                     8
    50 黑胡子海贼团                                 4
    20 草帽海贼团                                 19
    30 红心海贼团                                  7

7 rows selected
  • 右外连接(右边的表不加限制)
用(+)来实现, 这个+号可以这样来理解: + 表示补充,即哪个表有加号,这个表就是匹配
表。如果加号写在左表,右表就是全部显示,所以是右连接。

SQL> select d.deptno, d.dname ,count(e.ename) from dept d, emp e where e.deptno(+)=d.deptno 
	group by d.deptno, d.dname ;

DEPTNO DNAME                          COUNT(E.ENAME)
------ ------------------------------ --------------
    10 周刊少年Jump                                2
    40 红发海贼团                                  5
    45 黑桃海贼团                                  0
    60 D之一族                                     8
    50 黑胡子海贼团                                 4
    20 草帽海贼团                                 19
    30 红心海贼团                                  7

7 rows selected

-- 以上结果和左外连接相同,只是sql语句的书写方式不同

1.4 自连接

自连接: 通过给当前表设置不同的别名,将同一张表视为多张表来使用, 进行多表联查。(也就是自己和自己对比)

举例: 查询 xxx员工的老板是 yyy, 最后将尾田/岸本的老板也显示出来

-- emp 表中有一个字段叫做 mgr ,存放的是员工的老板数据,只要取出mgr中的数据对应到emp表中查询即可完成查询

SQL> select e.ename "员工", b.ename "老板" from emp e, emp b where e.mgr=b.empno;
-- 使用这一条语句可以发现结果中少了两个记录,因为尾田/岸本在表中没有老板,所以没有显示,这个时候要加上自连接

SQL> select e.ename "员工", b.ename "老板" from emp e, emp b where e.mgr=b.empno(+);
-- 自连接的理念和外连接一样,哪端的数据少就在哪端加(+),b表中缺少数据,所以在b表一端加上(+)
员工                                               老板
-------------------------------------------------- --------------------------------------------------
阿巴罗·皮萨罗                                      马歇尔·D·帝奇
芝沙斯·巴沙斯                                      马歇尔·D·帝奇
岸本_齐史                                          
尾田_荣一郎                                        

45 rows selected

-- 如果要想给尾田/岸本在查询后增加个显示,需要再搭配nvl使用
SQL> select e.ename "员工", nvl(b.ename,'他老婆') "老板" from emp e, emp b where e.mgr=b.empno(+);
员工                                               老板
-------------------------------------------------- --------------------------------------------------
阿巴罗·皮萨罗                                      马歇尔·D·帝奇
芝沙斯·巴沙斯                                      马歇尔·D·帝奇
岸本_齐史                                          他老婆
尾田_荣一郎                                        他老婆

45 rows selected

2. 子查询

子查询就是select 语句的嵌套使用, 即: 在select中使用select。 表示子语句的select需要写在()中
写到()中的select执行的优先级高

2.1 同表子查询

举例: 查询比 路飞 工资高的员工信息

-- 先查出路飞的工资是多少
SQL> select sal from emp where ename like '%路飞%';

      SAL
---------
  5450.00

-- 然后再和路飞的工资比较得出查询
SQL> select ename, sal from emp where sal>(select sal from emp where ename like '%路飞%');

ENAME                                                    SAL
-------------------------------------------------- ---------
妮可·罗宾                                           5500.00
特拉法尔加·D·瓦铁尔·罗                             5655.00
香克斯                                               6000.00
本·贝克曼                                           5500.00
耶稣布                                               5500.00
马歇尔·D·帝奇                                       5675.00
Monkey·D·Luffy                                     5500.00
Trafalgar·D·Water·Law                             5800.00
Marshall·D·Teach                                   7800.00
Portgas·D·Ace                                      5500.00
Gol·D·Roger                                        7500.00
Monkey·D·Dragon                                    6550.00
Monkey·D·Garp                                      7890.00

13 rows selected

2.2 不同表子查询

举例: 查询部门名称是 草帽海贼团 的员工信息

-- 先查询部门名称是 草帽海贼团 的部门编号
SQL> select deptno from dept where dname='草帽海贼团';

DEPTNO
------
    20

-- 通过部门编号过滤得到 草帽海贼团 的员工信息
SQL> select * from emp where deptno=(select deptno from dept where dname='草帽海贼团');

2.3 在select、from、where、having后边使用子查询

  • 在select后使用子查询 - 不常用

查询20号部门的员工号、员工姓名、部门编号、部门名称

-- 员工信息: emp表, 可以得到 员工号、员工姓名、部门编号, 部门名称在dept表
select empno, ename, deptno, (select dname from dept where deptno = 20)
 from emp
where deptno = 20;
  • 在from后使用子查询 – 更不常用

在使用select进行查询的时候, from关键字值可以 指定表名 或者 一个结果集 即查询的结果. 子
查询的结果就是一个结果集, 因此可以将子查询直接写到from的后边.

-- 举例
select ename, sal from (select ename, sal, sal*12 from emp);
  • where后使用子查询 --> 常用的方式

查询比平均工资高的员工信息

-- 首先查询出平均工资
SQL> select avg(sal) from emp;

  AVG(SAL)
----------
3932.03733

-- 然后用在emp表中判断工资是否大于平均工资
SQL> select ename, sal from emp where sal>(select avg(sal) from emp);

ENAME                                                    SAL
-------------------------------------------------- ---------
娜美                                                 4560.00
蒙奇·D·路飞                                         5450.00
妮可·罗宾                                           5500.00


  • 在having后使用子查询

分组统计需要使用关键字: group by , 在这个关键字后边进行数据过滤需要使用关键字 having

查询部门的平均薪资, 并且该部门的平均薪资高于30号部门的平均薪资

-- 首先求出30号部门的平均薪资
SQL> select avg(sal) from emp where deptno=30;

  AVG(SAL)
----------
3322.14285

-- 再分组统计每个部门的平均薪资与30号部门的平均薪资比较
SQL> select deptno, avg(sal) from emp group by deptno 
	having avg(sal)>(select avg(sal) from emp where deptno=30);

DEPTNO   AVG(SAL)
------ ----------
    40       4640
    50     3996.5
    10       5000
    60    6248.75
    

-- 如果再增加需求:在结果集中, 显示部门名称和部门ID
-- 需要使用多表查询,因为部门名称在dept表中
SQL> select d.deptno, d.dname, avg(e.sal)
  from emp e, dept d
 where d.deptno = e.deptno
 group by d.deptno, d.dname
having avg(e.sal) > (select avg(sal) from emp where deptno = 30);

DEPTNO DNAME                          AVG(E.SAL)
------ ------------------------------ ----------
    10 周刊少年Jump                         5000
    40 红发海贼团                           4640
    60 D之一族                           6248.75
    50 黑胡子海贼团                        3996.5

查询部门的最低月薪并且该部门最低月薪高于50号部门的最低月薪

-- 首先查询50号部门的最低月薪,再用于月薪的比较判断
SQL> select d.deptno, d.dname, min(e.sal)
  from emp e, dept d
 where d.deptno = e.deptno
 group by d.deptno, d.dname
having min(sal) > (select min(sal) from emp where deptno = 50);

DEPTNO DNAME                          MIN(E.SAL)
------ ------------------------------ ----------
    10 周刊少年Jump                         5000
    40 红发海贼团                           2600
    60 D之一族                              3450

2.4 单行/多行子查询

  • 单行子查询

单行子查询就是 该条子查询执行结束时,只返回一条记录(一行数据)。
在当行子查询中只能使用单行操作符: 使用单行操作符: = 、 > 、 >= 、 < 、 <= 、 <>

上面的例子全是单行子查询

举例: 查询工资比 路飞 工资高的员工的信息

-- 先查出路飞的工资是多少
SQL> select sal from emp where ename like '%路飞%';

      SAL
---------
  5450.00

-- 然后再和路飞的工资比较得出查询
SQL> select ename, sal from emp where sal>(select sal from emp where ename like '%路飞%');

  • 多行子查询

多行子查询就是 该条子查询执行结束时,只返回多条记录(多行数据)。
多行操作符有:

  • IN: 等于列表中的 任意 一个
  • ANY: 和子查询返回的 某一个 值比较
  • ALL: 和子查询返回的 所有 值比较

与每个比较运算符一起使用时的含义:

条件 表示含义
c = ANY (…) c列中的值必须与集合中的一个或多个值匹配,以评估为true
c != ANY (…) c列中的值不能与集合中的一个或多个值匹配以评估为true
c > ANY (…) c列中的值必须大于要评估为true的集合中的最小值。
c < ANY (…) c列中的值必须小于要评估为true的集合中的最大值。
c>= ANY (…) c列中的值必须大于或等于要评估为true的集合中的最小值。
c <= ANY (…) c列中的值必须小于或等于要评估为true的集合中的最大值。

下表说明了SQL ALL运算符的含义:

条件 描述
c > ALL(…) c列中的值必须大于要评估为true的集合中的最大值。
c >= ALL(…) c列中的值必须大于或等于要评估为true的集合中的最大值。
c < ALL(…) c列中的值必须小于要评估为true的集合中的最小值。
c <= ALL(…) c列中的值必须小于或等于要评估为true的集合中的最小值。
c <> ALL(…) c列中的值不得等于要评估为true的集合中的任何值。
c = ALL(…) c列中的值必须等于要评估为true的集合中的任何值。
  1. 查询部门名称为 红心海贼团 和 红发海贼团 的员工信息
-- 先查询出部门名称为 红心海贼团 和 红发海贼团 的部门编号,是一个集合
SQL> select deptno from dept where dname in ('红心海贼团', '红发海贼团');

DEPTNO
------
    30
    40
    
-- 再根据这个部门编号集合查询对应的员工信息
SQL> select * from emp where deptno 
	in (select deptno from dept where dname in ('红心海贼团', '红发海贼团'));
	
  1. 查询所有月薪比30号部门薪资最低者的工资高的员工信息
------------------ 单行子查询处理思路 ------------------
-- 查询出30号部门工资最低者的工资
SQL> select min(sal) from emp where deptno=30;

  MIN(SAL)
----------
      2050
-- 再通过这个最低工资来进行过滤
SQL> select * from emp where sal>(select min(sal) from emp where deptno=30);

------------------ 单行子查询处理思路 ------------------
-- 查出30号部门的所有人的工资,组成一个集合
select sal from emp where deptno=30;
-- 通过这个集合来进行过滤,可以使用 any 关键字
SQL> select * from emp where sal>any(select sal from emp where deptno=30);

注意:>any() 表示大于any中的最小值即为true
  1. 查询比30号部门所有员工工资都高的员工信息
------------------ 单行子查询处理思路 ------------------
-- 查询出30号部门工资最高者的工资
SQL> select max(sal) from emp where deptno=30;

-- 再通过这个最低工资来进行过滤
SQL> select * from emp where sal>(select max(sal) from emp where deptno=30);

------------------ 单行子查询处理思路 ------------------
-- 查出30号部门的所有人的工资,组成一个集合
select sal from emp where deptno=30;
-- 通过这个集合来进行过滤,可以使用 any 关键字
SQL> select * from emp where sal>all(select sal from emp where deptno=30);

注意:>all() 表示大于any中的最大值即为true

2.5 子查询中的NULL

判断一个值等于、不等于空,不能使用=和!=号,而应该使用is 和 not。

如果集合中有NULL值, 不能使用not in。例如:not in (10, 20, NULL),但是可以使用 in

查询不是管理者的员工信息。

-- 查询的表是员工信息表-> emp
-- 如何确定员工是是不是管理者: 在数据库表的 mgr 字段的值对应的是员工的编号, 只要员工编号出现在这一列, 这个员工就是一个管理者

-- 查询是管理者的员工的信息
-- 这个查询对应的结果集中有空字段null,注意使用distinct关键字,因为不同员工有相同的管理者
SQL> select distinct mgr from emp;

  MGR
-----
 7839
 7698
 7902
 7566
 6100

6 rows selected

-- 上面的结果显示有6条查询,但只有5条显示,因为结果集中有null字段。
-- 所以注意要将mgr是空的条目过滤掉,使用is not null
SQL> select * from emp where empno not in(select distinct mgr from emp where mgr is not null);

3. 集合运算

3.1 交集、并集、差集

关键字: 并集: union, 全并集: union all, 交集: intersect, 差集: minus

比如说有集合 A ( 1 , 2 , 3 ) , B ( 2 , 3 , 4 ):

  • A∪B(取并集) = ( 1, 2, 3, 4) ,如果是全并集就是 (1, 2, 3, 2, 3, 4)

  • A∩B (取交集) = ( 2, 3 )

  • A – B(取差集) = ( 1 ) ,B – A = (4)

-- 并集
select * from emp where deptno=10 
	union
select * from emp where deptno=20;

-- 全并集
select * from emp where deptno=10 
	union all
select * from emp where deptno in(10,20);

-- 交集
select * from emp where deptno=10 
	intersect
select * from emp where deptno in(10,20);

-- 差集, 剩下30号部门信息
select * from emp where deptno in(10, 30) 
	minus
select * from emp where deptno in(10,20);

-- 交换位置, 剩下20号部门信息
select * from emp where deptno in(10, 20) 
	minus
select * from emp where deptno in(10,30);

3.2 集合使用的注意事项

  1. 参与运算的各个集合必须列数相同,且类型一致

    -- 错误的例子
    -- 两个集合列数不一致, 
    select ename, deptno from emp where deptno=10 
    	union
    select * from emp where deptno=20;
    
    -- 查询的数据类型不一致
    select ename, deptno from emp where deptno=10 
    	union
    select deptno, ename from emp where deptno=20;
    
  2. 采用第一个集合的表头作为最终使用的表头。

    select ename "员工信息", deptno "部门ID" from emp where deptno=10 
    	union
    select ename "Name", deptno "ID" from emp where deptno=20;
    
  3. 可以使用括号()先执行后面的语句。

    select ename "员工信息", deptno "部门ID" from emp where deptno=10 
    	union
    

(select ename “Name”, deptno “ID” from emp where deptno=20);








# 4. 数据处理

## 4.1 插入数据

```sql
-- 语法格式:
insert into 表名[列名1,列名2,...] values(列值1,列值2,...);
SQL> desc bonus;
Name  Type         Nullable Default Comments 
----- ------------ -------- ------- -------- 
ENAME VARCHAR2(10) Y                         
JOB   VARCHAR2(9)  Y                         
SAL   NUMBER       Y                         
COMM  NUMBER       Y         

上述是一张空表,可以用来测试数据处理。

  • 插入全部列
-- 给所有的列指定出数据值,这时表名后面的列名可以不写
SQL> insert into bonus values('lily','后台',10000,10000);

SQL> select * from bonus;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------
lily       后台           10000      10000
  • 插入部分列/隐式插入null
-- 在插入数据的时候可指定部分列的值, 没有指定值的字段就是空字段
-- 如果指定部分列的值, 需要将列名一并写出,写在表名后面
SQL> insert into bonus(ename, sal, comm) values('tom', 20000, 10000);

1 row inserted

SQL> select * from bonus;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------
lily       后台           10000      10000
tom                       20000      10000

  • 显示插入null
-- 可以显示的指定某一个字段值为空
SQL> insert into bonus values('tom', null, 20000, 10000);

1 row inserted


SQL> select * from bonus;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------
lily       后台           10000      10000
tom                       20000      10000
tom                       20000      10000

4.2 更新数据

-- 语法结构:
update 表名 set 列名1=列值[,列名2=列值,...] where 列名=列值(条件);
SQL> select * from bonus;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------
lily       后台           10000      10000
tom                       20000      10000
tom                       20000      10000

SQL> update bonus set sal=25000 where ename='tom';

2 rows updated


SQL> select * from bonus;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------
lily       后台           10000      10000
tom                       25000      10000
tom                       25000      10000

4.3 删除数据

-- 语法格式:
delete from 表名;     -- 表示删除表中的全部数据

delete from 表名 where 列名=列值(条件);   -- 表示按条件筛选删除


-- 举个栗子
SQL> delete from bonus where ename='lily';

1 row deleted


SQL> select * from bonus;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------
tom                       25000      10000
tom                       25000      10000

SQL> delete from bonus;

2 rows deleted


SQL> select * from bonus;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------

4.4 事务

4.4.1 什么是数据库事务

数据库事务是构成单一逻辑工作单元的操作集合

  • 多个操作的集合, 这些操作中必须要涉及到数据库数据的写操作
  • 如果都是读操作, 根本不需要事务

比如: 转账是生活中常见的操作,比如从A账户转账100元到B账号。站在用户角度而言,这是一个逻辑上的单一操作,然而在数据库系统中,至少会分成两个步骤来完成:

  1. 将A账户的金额减少100元

  2. 将B账户的金额增加100元。

在这个过程中可能会出现以下问题:

  1. 转账操作的第一步执行成功, A账户上的钱减少了100元, 但是第二步执行失败或者未执行便发生系统崩溃,导致B账户并没有相应增加100元。
  2. 转账操作刚完成就发生系统崩溃,系统重启恢复时丢失了崩溃前的转账记录。
  3. 同时又另一个用户转账给B账户,由于同时对B账户进行操作,导致B账户金额出现异常。
  4. 这个交易的过程不能出现任何异常
    • 如果出现了异常, 需要进行状态的回滚, 回滚到交易开始的时候 == > 复原
    • 如何有事务就可以还原状态, 如果没有事务就无法还原
  5. 事务的作用:
    • 在交易开始的时候开启一事务, 记录当前的状态
    • 交易过程中判断每一步是否成功了, 成功: 继续, 失败: 不继续交易, 状态回滚到开始的时候

为了便于解决这些问题,需要引入数据库事务的概念。

关于事务有以下几点特性:

  1. 数据库事务可以包含一个或多个数据库操作, 但这些操作构成一个逻辑上的整体,构成逻辑整体的这些数据库操作, 要么全部执行成功, 要么全部不执行。 – 原子性
  2. 构成事务的所有操作, 要么全都对数据库产生影响, 要么全都不产生影响, 即不管事务是否执行成功, 数据库总能保持一致性状态。 – 一致性
  3. 事务的执行不受其他事务的干扰,当数据库被多个客户端并发访问时,会隔离它们的操作,防止出现:脏读、幻读、不可重复读 。 – 隔离性
  • 脏读是指一个事务读取了另一个事务未提交的数据
  • 不可重复读是指一个事务对同一数据的读取结果前后不一致。
  • 幻读是指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致。
  • 幻读和不可重复读的区别在于,不可重复读是针对确定的某一行数据而言, 而幻读是针对不确定的多行数据。
  1. 对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。–持久性

alin的学习之路(数据库篇:三)(多表查询,子查询,集合运算,数据处理)

alin的学习之路(数据库篇:三)(多表查询,子查询,集合运算,数据处理)

4.4.2 oracle中的事务

  • 设置保存点

    -- 在写数据库之前, 需要设置保存点 ===> 开启了一个事务
    -- 保存点的名字不允许重名
    sql> savepoint 保存点的名字;
    
  • 回滚到保存点

    -- 事务对应的操作集合有一步失败了, 需要进行数据的回滚
    -- 回滚就是内存数据的覆盖, 用旧的数据覆盖新的数据
    

sql> rollback to savepoint 保存点的名字;


- 提交事务

```sql
-- 提交之后保存点就不存在了, 提交之后不能回滚
-- 提交就是将内存数据写入到磁盘的数据库文件中
sql> commit;
  • 举例

    -- 一个完整的操作(一个事务), 这个操作需要若干步来完成
    -- 设置一个保存点 a
    -- 状态: 空表
    SQL> savepoint a;
    
    Savepoint created
    
    -- 插入数据
    SQL> insert into bonus values('tom', '董事长', 10000, 2000);
    
    1 row inserted
    
    
    SQL> insert into bonus values('jack', '销售', 1000, 2000);
    
    1 row inserted
    
    -- 添加保存点 b
    -- 状态: 数据库表中 有两条记录
    SQL> savepoint b;
    
    Savepoint created
    
    SQL> insert into bonus values('lucy', '秘书', 10000, 20000);
    
    1 row inserted
    
    
    SQL> insert into bonus values('lily', '小秘书', 9000, 20000);
    
    1 row inserted
    
    -- 添加保存点c
    -- 数据库表中有4条数据
    SQL> savepoint c;
    
    Savepoint created
    
    
    SQL> delete from bonus where ename = 'lily';
    
    1 row deleted
    
    SQL> select * from bonus;
    
    ENAME      JOB              SAL       COMM
    ---------- --------- ---------- ----------
    tom        董事长         10000       2000
    jack       销售            1000       2000
    lucy       秘书           10000      20000
    
    -- 回滚到保存点c
    -- 重新回到有4条记录的表状态
    SQL> rollback to savepoint c;
    
    Rollback complete
    
    
    SQL> select * from bonus;
    
    ENAME      JOB              SAL       COMM
    ---------- --------- ---------- ----------
    tom        董事长         10000       2000
    jack       销售            1000       2000
    lucy       秘书           10000      20000
    lily       小秘书          9000      20000
    
    
    -- 当确定所有的数据库操作都没有问题, 提交事务
    SQL> commit;
    
    Commit complete