1 数据库及数据准备
1.1 SQL和数据库概述
- DB:DataBase,数据库,在硬盘上以文件的形式存在
- DBMS:DataBase Management System,数据库管理系统,常见的有MYSQL、Oracle、SqlServer、DB2…
- SQL:Strutured Query Language,结构化查询语言,是一门高级语言,标准的SQL适用于所有数据库管理系统。SQL语句在执行时也会进行编译,编译由DBMS完成。
- 三者的关系:DBMS负责执行SQL语句,来操作DB中的数据。
1.2 表
- 表(table)是一种结构化文件,用来存储特定类型的数据,如学生信息、课程信息等。
- 表是数据库的基本组成单元,所有数据都以表格的形式组织,可读性强。
- 表都有特定的名称,而且不能重复。
- “列”叫做字段(column),“行”叫做记录/数据(data),每一个字段都有字段名称、字段数据类型、字段约束和字段长度。
1.3 SQL语句分类
- 数据查询语言DQL(Data Query Language):select,查询语句
- 数据操作语言DML(Data Manipulation Language):insert,delete,update,对表中数据进行增删改
- 数据定义语言DDL(Data Defination Language):create,drop,alter,对表结构进行增删改
- 事务控制语言TCL(Transaction Control Language):commit提交事务,rollback回滚事务
- 数据控制语言DCL(Data Control Language):grant授权,revoke撤销权限
1.4 导入数据
-
登陆mysql数据库管理系统
- dos命令窗口—>
mysql -uroot -p1234
- dos命令窗口—>
-
查看数据库
show databases;
(MySQL命令,不是SQL语句)
-
创建自己的数据库
creat database liuyupeng;
(MySQL命令,不是SQL语句)
-
使用数据库
use liuyupeng;
(MySQL命令,不是SQL语句)
-
查看当前使用的数据库有哪些表
show tables;
(MySQL命令,不是SQL语句)
-
初始化数据
-
source D:\Programming\MySQL\resources\bjpowernode.sql
-
初始化后,有3张表
mysql> show tables; +---------------------+ | Tables_in_liuyupeng | +---------------------+ | dept | | emp | | salgrade | +---------------------+ 3 rows in set (0.00 sec)
-
-
查看表的创建语句
show create table emp;
-
删除数据库
drop database liuyupeng;
1.5 sql脚本
扩展名是.sql,且其中编写了大量的sql语句的文件称为”sql脚本“。sql脚本数据量太大,无法打开的时候,可以直接使用source
命令执行sql脚本,完成初始化。
1.6 表结构
查看表结构:desc 表名称;
查看表中的数据:select * from 表名称;
2 常用命令
查看mysql版本:
C:\Users\Yupeng>mysql --version
mysql Ver 14.14 Distrib 5.5.36, for Win64 (x86)
C:\Users\Yupeng>mysql -V
mysql Ver 14.14 Distrib 5.5.36, for Win64 (x86)
创建数据库:
create database 数据库名称;
use 数据库名称;
查询当前使用的数据库:
select database();
终止一条正在编写的语句,可输入\c
。
3 简单查询
3.1 查询一个字段
select 字段名称 from 表名称;
,select语句后面跟的是字段名称,select是关键字,select和字段名称之间采用空格隔开,from表示将要查询的表,它和字段之间采用空格隔开。
注意:
- 任何一条sql语句以”;“结尾
- sql语句不区分大小写
3.2 查询多个字段
select 字段名称1,字段名称2,字段名称3... from 表名称;
,查询多个字段,select中的字段采用逗号隔开。
字段可以参与数学运算:select ename,sal * 12 from emp;
。
给查询结果的列重命名:select ename,sal * 12 as yearsalary from emp;
。
as关键字可以省略:select ename,sal * 12 yearsalary from emp;
。
如果重命名中有中文,标准sql语句要求用单引号将字符串括起来,select ename,sal * 12 as '年薪' from emp;
。
去除查询结果集中的重复记录,select distinct 字段名称 from 表名称;
,但distinct关键字只能出现在所有字段的最前面,去除所有联合字段的重复记录。不会改变源数据。统计岗位的数量:select count (distinct job) from emp;
3.3 查询全部字段
select * from 表名称;
,实际开发中不建议使用,效率较低。
4 条件查询
条件查询的语法格式:
select 字段名称1,字段名称2... from 表名称 where 条件;
支持以下运算符:
运算符 | 说明 |
---|---|
= | 等于 |
<>或!= | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
between … and …. | 两个值之间,等同于 >= and <= |
is null | 为null(is not null 不为空) |
and | 并且 |
or | 或者 |
in | 包含,相当于多个or(not in是不在这个范围中) |
not | not可以取非,主要用在is或in中 |
like | like称为模糊查询,支持%或下划线匹配,%匹配任意个字符,下划线只匹配一个字符 |
4.1 等号操作符
查询薪水为5000的员工
select ename from emp where sal = 5000;
查询某员工的薪水
select sal from emp where ename = 'SMITH';
4.2 <>操作符
查询薪水不等于5000的员工
select ename, sal from emp where sal <> 5000;
或者select ename, sal from emp where sal != 5000;
4.3 between … and ….操作符
用于数字,闭区间:查询薪水在1100到3000之间的员工
select ename, sal from emp where sal between 1100 and 3000;
(使用较少)用于字符串,左闭右开:查询员工的姓名
select ename from emp where ename between 'A' and 'D';
4.4 is null运算符
null不是一个值,不能用等号,必须使用is null或者is not null。
查询没有津贴的员工
select ename, sal, comm from emp where comm is null;
查询有津贴的员工
select ename, sal, comm from emp where comm is not null;
4.5 and和or(in)运算符
查询岗位是MANAGER和SALESMAN的员工
select ename, job from emp where job = 'MANAGER' or job = 'SALESMAN';
或者select ename, job from emp where job in ('MANAGER', 'SALESMAN')
。
查询薪水大于1000并且部门编号是20或30的员工
select ename, sal, deptno from emp where sal > 1000 and deptno = 20 or sal > 1000 and deptno = 30;
或者select ename, sal, deptno from emp where sal > 1000 and (deptno = 20 or deptno = 30);
,当运算符的优先级不确定时,加小括号。
4.6 模糊查询
模糊查询中,%代表任意多个字符,_代表任意1个字符
查询名字中含有’O‘的员工
select ename from emp where ename like '%O%';
查询名字的第三个字母是A的员工
select ename from emp where ename like '__A%';
查询名字中含有下划线’_'的员工
select ename from emp where ename like '%\_%';
,要加转义字符。
5 排序数据
5.1 单字段排序
按照薪水升序,查询员工名字和薪水
select ename, sal from emp order by sal;
,默认是升序。
可以指定排序顺序,asc表示升序,desc表示降序。select ename, sal from emp order by sal desc;
5.2 多字段排序
越靠前的字段起到主导作用。
按薪水降序排列,薪水相同时再按照名字升序排列。
select ename, sal from emp order by sal desc, ename asc;
查询工作岗位是SALESMAN的员工,且按照薪水降序排列。
select ename, job, sal from emp where job = 'salesman' order by sal desc;
,先执行from,再对比条件where,然后查询select,最后排序order。
5.3 使用字段顺序排序
select ename, sal from emp order by 2;
,按照第2列排序,也就是ename和sal中的第二个,即按照sal排序。不建议使用此种方式,因为采用数字含义不明确,程序不健壮。
6 分组函数
分组函数是对某一组数据进行操作。
分组函数也称多行处理函数,特点是输入多行,输出结果是一行。对比地,单行处理函数特点是输入几行,也输出几行。重点:只要有null参与的运算结果一定是null,处理方法是使用ifnull函数, 查询津贴select ename, ifnull(comm,0) as comm from emp;
,查询年薪select ename, (sal+ifnull(comm,0))*12 as yearsal from emp;
分组函数自动忽略null。
函数名称 | 作用 |
---|---|
count | 取得记录数 |
sum | 求和 |
avg | 取平均 |
max | 取最大值 |
min | 取最小值 |
查询薪水总和
select sum(sal) from emp;
查询最高薪水
select max(sal) from emp;
查询平均薪水
select avg(sal) from emp;
查询员工总人数
select count(*) from emp;
或者select count(ename) from emp;
组合使用
select count(*), max(sal), min(sal), avg(sal), sum(sal) from emp;
注意:语法规则:分组函数不能直接使用在where关键字后面。查询薪水高于平均薪水的员工:select ename,sal from emp where sal > avg(sal);
,报错:ERROR 1111 (HY000): Invalid use of group function
,正确写法:select ename, sal from emp where sal > (select avg(sal) from emp);
。
count(*)
和count(某个字段)
的区别:count(*)
是统计总记录条数,和具体字段无关,count(某个字段)
统计某个字段不为null的记录条数。
7 分组查询
7.1 group by
group by:按照某个字段或某些字段进行分组。
注意:1.分组函数一般和group by联合使用,且分组函数是在group by语句执行结束后执行的;2.如果一条sql语句没有group by的话,整张表的数据会缺省分为一组;3.在SQL语句中若有group by 语句,那么在select语句后面只能跟分组函数和参与分组的字段,不能跟其他字段。比如select ename, max(sal), job from emp group by job;
,该语句在mysql中是有查询结果的,但结果没有意义,而在Oracle数据库中会报语法错误。
查询每个岗位的最高薪水
select job,sal from emp group by job;
查询每个工作岗位的平均薪水
select job, avg(sal) from emp group by job;
查询各个部门不同岗位的最高薪水
select deptno, job, max(sal) from emp group by deptno, job;
7.2 having
having:对分组之后的数据进行再次过滤,不可以单独出现。
查询每个部门的最高薪水,要求显示薪水大于2900的数据
select deptno, max(sal) from emp group by deptno having max(sal) > 2900;
或者
select deptno, max(sal) from emp where sal > 2900 group by deptno;
,后者效率较高,建议能使用where的时候尽量使用此方式。
查询每个部门的平均薪水,要求显示薪水大于2000的数据
select deptno, avg(sal) from emp group by deptno having avg(sal) > 2000;
,此时不能使用where,select deptno, avg(sal) from emp where avg(sal) > 2000 group by deptno;
,因为where后面不能直接跟分组函数。
7.3 select总结
一个完整的select语句格式如下
select 字段名称 from 表名 where... group by... having... order by...
以上语句的执行顺序:
- 执行from,从某张表查询
- 执行where,过滤原始数据
- 执行group by,进行分组
- 执行having,对分组后数据进行再次过滤
- 执行select,选出数据
- 执行order by,排序
8 连接查询
连接查询:在实际开发中,大部分情况下都不是从单表中查询数据,一般都是多张表联合查询取出最终的结果。
笛卡尔积现象:当两张表进行连接查询的时候,如果没有任何条件限制,最终的查询结果条数是两张表记录条数的乘积。查询每个员工的部门名称,要求显式员工名字和部门名字:select ename, dname from emp, dept;
,出现14 * 4 = 56条记录。避免笛卡尔积现象的方法是加条件进行过滤,避免后只显示有效记录,但不会减少匹配次数。
使用表的别名:两个优点,一是执行效率高,二是可读性好。select e.ename, d.dname from emp e, dept d;
连接查询的分类:
- 根据语法出现的时间可分为:
- SQL92
- SQL99
- 根据表的连接方式可划分为:
- 内连接
- 等值连接
- 非等值连接
- 自连接
- 外连接
- 左外连接
- 右外连接
- 全连接
- 内连接
8.1 内连接
假设A表和B表连接,使用内连接,A表和B表能够匹配的记录都查询出来,两张表没有主副之分。如果A表中有某条记录,而B表中没有匹配的记录,则最终查询结果会丢失这条记录。
8.1.1 等值连接
连接条件是等量关系。语法格式:... A join B on 连接条件 where ...
,将表的连接条件和where条件分离,使结构更清晰。
查询每个员工的部门名称,要求显示员工名字和部门名字:select e.ename, d.dname from emp e (inner,可省略)join dept d on e.deptno = d.deptno;
8.1.2 非等值连接
连接条件是非等量关系。
查询每个员工的工资等级,要求显示员工名字、工资和工资等级:select e.ename, e.sal, s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal;
8.1.3 自连接
特点是一张表看作两张表,自己连接自己。
查询每个员工的上级领导,要求显示员工名字和对应的领导名字:select a.ename as '员工名', b.ename as '领导名' from emp a inner join emp b on a.mgr = b.empno;
8.2 外连接
假设A表和B表连接,使用外连接,两张表中有一张是主表,另一张是副表,主要查询主表数据,主表的记录可以都查询出来,如果副表中有与A表匹配的记录,则查询显示,如果有的记录没有匹配上,则模拟出null与之匹配(这里对比内连接来理解)。外连接比内连接使用得多。
8.2.1 左外连接
左外连接也称左连接,表示左边的表是主表。
查询每个员工的上级领导(所有员工)select a.ename as '员工', b.ename as '领导' from emp a left (outer,可省略)join emp b on a.mgr = b.empno;
查询到14条记录。
如果采用内连接:select a.ename as '员工', b.ename as '领导' from emp a join emp b on a.mgr = b.empno;
,查询到13条记录,以为KING没有上级领导,会丢失1条记录。
8.2.2 右外连接
右外连接也称右连接,表示右边的表是主表。查询每个员工的上级领导(所有员工)select a.ename as '员工', b.ename as '领导' from emp b right join emp a on a.mgr = b.empno;
查询没有员工的部门select d.deptno from emp e right join dept d on d.deptno = e.deptno where e.empno is null;
或者select d.deptno from emp e right join dept d on d.deptno = e.deptno where e.ename is null;
8.3 三张表以上的连接
查询每个员工的部门名称以及薪水等级select e.ename, d.dname,s.grade from emp e join dept d on e.deptno = d.deptno join salgrade s on e.sal between s.losal and s.hisal;
。
查询每个员工的部门名称、薪水等级和上级领导select e.ename, a.ename '上级领导', d.dname,s.grade from emp e join dept d on e.deptno = d.deptno join salgrade s on e.sal between s.losal and s.hisal left join emp a on e.mgr = a.empno;
,注意此例中查询上级领导时要用外连接。
9 子查询
select语句中嵌套select语句,被嵌套的select语句是子查询。
9.1 where子句中使用子查询
查询高于平均薪水的员工信息select * from emp where sal > (select avg(sal) from emp);
9.2 from后面嵌套子查询
查询每个部门平均薪水的薪资等级
第一步,查询每个部门的平均薪水select deptno, avg(sal) as avgsal from emp group by deptno;
第二步,将以上查询结果作为临时表t,t表和salgrade s表连接。select t.*, s.grade from (select deptno, avg(sal) as avgsal from emp group by deptno) t join salgrade s on t.avgsal between s.losal and s.hisal
9.3 select后面嵌套子查询
查询每个员工所在的部门名称select e.ename, (select d.dname from dept d where e.deptno = d.deptno) as dname from emp e;
10 union
union可以将查询结果相加,两个select查询的字段数量应该一致。
查询工作岗位是SALESMAN和MANAGER的员工。
第一种:select ename, job from emp where job = 'SALESMAN' or job = 'MANAGER';
第二种:select ename, job from emp where job in('SALESMAN','MANAGER');
第三种:select ename, job from emp where job = 'MANAGER' union select ename, job from emp where job = 'SALESMAN';
11 limit(重点)
limit是mysql特有的,其他数据库没有。
limit取结果集中的部分数据。
limit是SQL语句最后执行的一个环节。
语法机制:limit startIndex, length
,startIndex表示起始位置(0表示第一条数据),length表示取的数量。startIndex不写时,默认从第一条数据开始获取。
查询薪水前5名的员工select ename, sal from emp order by sal desc limit 0, 5;
查询薪水第4名到第9名的员工select ename, sal from emp order by sal desc limit 3, 6;
通用分页SQL:每页显示pageSize条记录,第pageNo页:limit (pageNo - 1) * pageSize, pageSize
12 表
12.1 创建表
建表语句的语法格式:
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型,
...
);
表名一般建议以t_
或tbl_
开头。
常见的MySQL字段的数据类型:
int 整数型(对应java中的int)
bigint 长整型(对应java中的long)
float 浮点型(对应java中的float double)
char 定长字符串(对应java中的String)
varchar 可变长字符串(对应java中的StringBuffer/StringBuilder)
date 日期类型(对应java中的java.sql.date)
BLOB 二进制大对象(Binary Large Object,存储图片、视频等流媒体信息)(对应java中的Object)
CLOB 字符大对象(Charactter Large Object,存储较大文本)(对应java中的Object)
char和varchar的选择:在开发中,当某个字段的数据长度不发生改变时,如性别、生日等,采用char;当一个字段的数据长度不确定时,例如简介、姓名等,采用varchar,varchar动态分配长度,节省空间,但不如char效率高。
创建学生表:
create table t_student(
no bigint,
name varchar(255),
sex char(1),
classno varchar(255),
birth char(10)
);
创建表时,如果字段不赋默认值,默认为null。赋默认值的语法格式为:birth char(10) 1970-01-01
。
可以通过另一种方式创建表,将查询结果复制:create table 表名 as select语句
create table emp1 as select empno,ename from emp;
,将表emp中的empno和ename字段复制作为一张新表emp1。
12.2 添加、修改、删除数据
12.2.1 添加数据
insert语句插入数据:insert into 表名(字段名1,字段名2,字段名3...) values(值1,值2,值3...)
,要求字段数量和值的数量要相同,且数据类型要对应。
添加张三的学生信息:insert into t_student(no,name,sex,classno,birth) values(1,'zhangsan','1','gaosan1ban','1990-11-12');
添加李四的学生信息:insert into t_student(name,sex,classno,birth,no) values('lisi','1','gaosan1ban','1991-03-19',2);
,字段的顺序可以与表的默认顺序不同,但要和值的顺序对应。
添加王五的学生信息:insert into t_student(no,name,sex,classno) values(3,'wangwu','1','gaosan1ban');
,插入部分字段时,其他字段默认为null。
添加赵六的学生信息:insert into t_student values(4,'zhaoliu','0','gaosan2ban','1991-05-06');
,表明后面不加字段名时,values后面的值需要和表的默认顺序和类型一致。
一次添加多行数据,用逗号隔开:insert into t_student(no,name,sex,classno,birth) values(5,'xiaoqi','1','gaosan1ban','1991-08-12'),(6,'zhangba','0','gaosan2ban','1992-01-19');
批量添加:insert into 表名1 select * from 表名2
,将一张表中select查询出的数据插入到另一张表。
12.2.2 修改数据
update语句修改数据:update 表名 set 字段名1 = 值1,字段名2 = 值2... where 条件;
。不加判断条件时,更新整张表。
将学号为5的学生的班级改为gapsan2ban,生日改为1990-02-02:update t_student set classno = 'gaosan2ban',birth = '1990-02-02' where no = 5;
12.2.3 删除数据
delete语句山下湖数据:delete from 表名 where 条件
。不加判断条件时,全部删除。
删除学号为6的学生数据:delete from t_student where no = 6;
删除大数据量的表中的数据:truncate table 表名;
,表被截断,永久丢失,不可回滚!
13 约束
在创建表的时候,可以给表的字段添加相应的约束,目的是保证表中数据的合法性、有效性、完整性。
常见的约束:
- 非空约束,not null,约束的字段不能为null
- 唯一约束,unique,约束的字段不能重复
- 主键约束,primary key,约束的字段既不能为null,也不能重复
- 外键约束,foreign key
- 检查约束,check(不建议使用)(Oracle支持,在mysql中还不支持)
13.1 非空约束
drop table if exists t_user;
create table t_user(
id int,
username varchar(255) not null,
password varchar(255)
);
#以下代码会报错
insert into t_user(id,password) values(1,'123');
#错误信息:ERROR 1364 (HY000): Field 'username' doesn't have a default value,因为没有给username字段赋值,所以默认赋空值,而username字段要求非空
#正确写法:
insert into t_user(id,username,password) values(1,'zhangsan','123');
13.1 唯一约束
唯一约束修饰的字段具有唯一性,不可重复,但可以为null。
单个字段添加unique约束:列级约束
drop table if exists t_user;
create table t_user(
id int,
username varchar(255) unique
);
insert into t_user(id,username) values(1,'zhangsan');
insert into t_user(id,username) values(2,'zhangsan'); #报错:ERROR 1062 (23000): Duplicate entry 'zhangsan' for key 'username'
# 几个记录的username字段可以都为null
insert into t_user(id) values(2);
insert into t_user(id) values(3);
insert into t_user(id) values(4);
多个字段联合unique:表级约束。注意:非空约束只能加到单个字段后面,没有表级的。
drop table if exists t_user;
create table t_user(
id int,
usercode varchar(255),
username varchar(255),
unique(usercode,username)
);
#以下代码是可执行的
insert into t_user values(1,'111','zhangsan');
insert into t_user values(1,'111','lisi');
insert into t_user values(1,'222','zhangsan');
/*如果创建代码是两个字段分开unique,以下代码就会报错:ERROR 1062 (23000): Duplicate entry '111' for key 'usercode'
ERROR 1062 (23000): Duplicate entry 'zhangsan' for key 'username'*/
drop table if exists t_user;
create table t_user(
id int,
usercode varchar(255) unique,
username varchar(255) unique
);
insert into t_user values(1,'111','zhangsan');
insert into t_user values(1,'111','lisi');
insert into t_user values(1,'222','zhangsan');
13.2 主键约束
主键约束:primary key,约束的字段既不能为null,也不能重复。
主键字段:被主键约束修饰的字段
主键值:主键字段的每一个值都是主键值
表的设计三范式的第一范式就要求任何一张表都应该有主键,主键值是一条记录在这张表中的唯一标识(就像人的身份证,其他信息都可以相同,但身份证号不能相同)。
注意:一张表的主键约束只能有1个!
主键的分类:
- 根据主键字段的数量:
- 单一主键
- 复合主键
- 根据主键性质:
- 自然主键,主键值是一个和业务没有关系的自然数。
- 业务主键,主键值和系统的业务挂钩,不推荐用,最好不要将和业务挂钩的字段作为主键,因为业务一旦发生变化,主键值可能也需要随之变化,但有时候无法变化,因为变化可能导致主键值重复,解决方法是设立自然主键,而将和业务挂钩的主键设为唯一约束。
drop table if exists t_user;
create table t_user(
id int primary key,
username varchar(255),
email varchar(255)
);
insert into t_user(id,username,email) values(1,'zs','zs@123.com');
insert into t_user(id,username,email) values(2,'ls','ls@123.com');
insert into t_user(id,username,email) values(3,'ww','ww@123.com');
#报错:ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY',主键重复
insert into t_user(id,username,email) values(1,'zl','zl@123.com');
#报错:ERROR 1364 (HY000): Field 'id' doesn't have a default value,主键不能为空
insert into t_user(username,email) values('lb','lb@123.com');
mysql的主键值自增:id int primary key auto_increment
,id字段自动维护一个自增的数字,从1开始,以1递增。Oracle中也提供了自增机制:序列(sequence)。
13.3 外键约束
外键约束:foreign key
外键字段:添加有外键约束的字段
外键值:外键约束中的每一个值,外键值可以为null
语法格式:子表字段名x 字段类型 foreign key (x) references 父表名(父表字段名)
注意:被引用的字段不一定是主键,但至少有unique约束。
#先创建父表
drop table if exists t_classes;
create table t_classes(
classes_id int(3),
classes_name varchar(30) not null,
constraint pk_classes_id primary key(classes_id)
)
#再创建子表
drop table if exists t_student;
create table t_student(
student_id int(10),
student_name varchar(50) not null,
sex char(2) not null,
birthday date not null,
email varchar(30) unique,
classes_id int (3) not null,
constraint pk_student_id primary key(student_id),
constraint fk_classes_id foreign key(classes_id) references t_classes(classes_id)
)
14 事务
- 事务是一个完整的业务逻辑单元,不可再分。
- 和事务相关的语句只有数据操作语言DML语句(insert、delete、update),因为它们是和数据库表中的数据相关的。
- 通常一个事务需要多条DML语句共同联合完成。
- 事务的四大特性ACID:
- 原子性(Atomicity):事务是最小的工作单元,不可再分。
- 一致性(Consistency):事务必须保证多条DML语句同时成功或同时失败。
- 隔离性(Isolation):事务之间具有隔离,一个事务不会影响其他事务的运行。
- 持久性(Durability):最终数据必须持久化到硬盘中,事务才算结束。
14.1 事务的隔离级别
事务的隔离性存在隔离级别,理论上隔离级别包括4个:
- 第一级别,读未提交(read uncommitted),允许一个事务可以看到其他事务未提交的修改。这一级别存在脏读现象(Dirty Read)现象:读到了脏的数据。
- 第二级别,读已提交(read committed),允许一个事务只能看到其他事务已经提交的修改,未提交的修改是不可见的。解决了脏读现象。但问题是不可重复读旧数据。
- 第三级别,可重复读(repeatable read),解决了不可重复读的问题。但问题是读取到的数据是幻象。
- 第四级别,序列化/串行化(serializable),解决以上问题,但效率低,需要事务排队。
MySQL的默认隔离级别是可重复读,Oracle的默认隔离级别是读已提交。
14.2 演示事务
MySQL事务默认情况下是自动提交的,即只要执行任意一条DML语句,则提交一次。关闭自动提交:start transaction;
#准备表
drop table if exists t_user;
create table t_user(
id int primary key auto_increment,
username varchar(255)
);
#插入一条数据(默认提交)
insert into t_user(username) values('zhangsan');
#查看表
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
+----+----------+
#回滚
rollback;
#回滚之后,数据还在,因为回滚之前已默认提交
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
+----+----------+
#关闭自动提交,一个事务开启
start transaction;
#插入两条数据
insert into t_user(username) values('lisi');
insert into t_user(username) values('wangwu');
#回滚
rollback;
#查看表,还是一条数据,插入的两条数据并没有持久化到硬盘中
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
+----+----------+
#重新开启事务
start transaction;
#插入3条数据
insert into t_user(username) values('zhaoliu');
insert into t_user(username) values('xiaoqi');
insert into t_user(username) values('xiaoba');
#提交
commit;
#查看数据,4条记录
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
| 4 | zhaoliu |
| 5 | xiaoqi |
| 6 | xiaoba |
+----+----------+
#提交以后已经持久化到硬盘了,再回滚查看数据,还是4条记录
rollback;
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
| 4 | zhaoliu |
| 5 | xiaoqi |
| 6 | xiaoba |
+----+----------+
15 索引
15.1 索引原理
- 索引相当于一本书的目录,通过目录可以快速找到对应资源。
- 在数据库中,查询一张表有两种检索方式:全表扫描和根据索引检索(效率很高)。
- 索引的检索效率高是因为缩小了扫描范围。
- 索引虽然可以提高检索效率,但是不能随意的添加索引,比如表中的数据需要经常被修改时,就不适合添加索引,因为数据一旦修改,索引需要重新排序,维护成本高。
索引的实现原理:
索引底层的数据结构是B + Tree,通过B Tree缩小扫描范围,底层索引对记录进行排序和分区,索引会携带数据在表中的“物理地址”;通过索引检索到数据之后,由关联的物理地址定位到表中的数据,效率是最高的。
15.2 索引的使用
1、什么时候考虑给字段添加索引?
- 数据量庞大。(根据客户需求和线上环境)
- 该字段很少的DML操作。(因为字段进行修改操作后,索引也需要维护)
- 该字段经常出现在where子句中。(经常根据哪个字段查询)
select ename,sal from emp where ename = 'SMITH';
,当ename字段上没有添加索引的时候,以上sql语句会进行全表扫描,扫描ename字段中所有的值;当ename字段上添加索引的时候,以上sql语句会根据索引扫描,快速定位。
2、创建索引的语法格式:create index 索引名 on 表名(字段名);
,删除索引的语法格式:``drop index 索引名 on 表名`
3、索引的分类:
- 单一索引:给单个字段添加索引
- 复合索引:给多个字段联合添加一个索引
- 主键索引:主键上会自动添加索引,所以根据主键查询效率较高
- 唯一索引:有unique约束的字段上会自动添加索引
4、索引失效:模糊查询的时候,第一个通配符使用的是%时,索引是失效的:select ename from emp where ename like '%A%';
16 视图(了解)
1、视图是通过不同的角度看待同一张表的数据。
2、创建视图:create view 视图名 as select语句
,删除视图:drop view 视图名
。
3、对视图进行CRUD操作,会影响到基表数据。
4、视图作用:视图可以隐藏表的实现细节。例如保密级别较高的系统,数据库只对外提供相关的视图,程序员只对视图对象进行CRUD。
17 数据库设计三范式
设计范式是设计表的依据,按照三范式设计的表不会出现数据冗余。
第一范式:任何一张表都应该有主键,并且每一个字段原子性不可再分。
第二范式:建立在第一范式的基础之上,所有非主键字段完全依赖主键,不能产生部分依赖。多对多:三张表,关系表有两个外键。
第三范式:建立在第二范式的基础之上,所有非主键字段直接依赖主键,不能产生传递依赖。一对多:两张表,多的表加外键。
提醒:在实际的开发中,以满足客户的需求为主,有时会拿冗余换执行速度。
写在最后:本笔记整理于B站——动力节点视频,感谢!
https://www.bilibili.com/video/BV1fx411X7BD?p=1