数据库笔记整理

Mysql相关概念

基本概念

  DB( database ) : 存储数据的仓库 , 其中的数据是有组织有关联的

  DBMS( database management system ) : 数据库管理系统 , 管理DB

  SQL( structure query language ) : 结构化查询语言 , 专门与DB通信的语言 , 多有DBMS( MySQL , Oracle , SQLserver等 )都支持

数据库分类

存储位置的不同进行分类 :

1. 基于磁盘的存储 , MySQL , Oracle , SQLsever

2. 基于内存的存储 , redis非常适合作缓存

从数据之间是否存在关系进行分类 : 

1. 关系型数据库 : MySQL , Oracle , SQLsever

2. 非关系型数据库 : redis , mongodb , nosql( not only sq )

数据表

数据库中有数据表 , 数据表由行和列组成 , 表中的每一列称为字段 , 每一列类似Java中的属性 , 每一行类似Java中的对象

字段的数据类型

1. 整形

 

  • tinyint(2) 等同于byte 取值范围 -128 - 127
  • tinyint(1) 等同于boolean 取值范围 0 , 1
  • int(n) n : 查询的时候单元格宽度 , int(11)显示宽度和数据类型的取值范围时无关的 , 如果插入了大于显示宽度的值 , 只要该值不超过该类型整数的取值范围 , 数值依然可以插入 , 而且能够显示出来
  • bigint(n) : 等同于long 常用于记录id , 时间的毫秒数

2. 小数

  • float(M,D) M称为精度 , 表示总共的位数 , D表示标度 , 表示小数的位数 , 
  • double(m,d)
  • decimal(m,n) BigDecimale DECIMALE的存储空间并不是固定的 , 而且精度值M决定 , 占用M+2个字节

3. 字符型

  • char(n) 固定长度 , char(4)不管是存入几个字符 , 都将占用4个字符
  • VARCHAR和TEXT类型是变长类型 , 其存储需求取决于列值的实际长度 , 一个VARCHAR(10)列能保存一个最大长度为10个字符的字符串
  • MySQL不区分字符和字符串 , 单引号和双引号类似

4. 日期型

 

  • date : 年月日
  • datetime : 年月日 时分秒
  • timestamp : 年月日 时分秒 时间戳

SQL语句

结构化查询语言 , sql语句不区分大小写

DDL(Data Definition Language) : 数据定义语言 , 用来定义数据库对象 : 库, 表, 列等; create drop alter

DML(Data Manipulation Language) : 数据操作语言 , 用来定义数据库记录(数据) insert delete update

DQL(Data Query Language) :  数据查询语言 , 用来查询记录select

DCL(Data Controler Language) : 数据控制语言 , 用来定义访问权限和安全级别; commit rollback

DDL

基本操作(库)

1. 创建数据库 :

  create database[if not exists]数据库名; (数据库名称不可改变)

2. 删除数据库:

  drop database 数据库名;(连着表/数据都一起删除, 数据无法回滚)

3. 查看创建数据库基本信息

  show create database 数据库名;

基本操作(表)

1. 创建表

  create table 表名(

  字段1 数据类型 [约束],

  字段2 数据类型 [约束],

  ...

  字段n 数据类型 [约束] 

    ); 

2. 表结构 alter

  新增新的字段

  alter table 表名 add 字段名  数据类型 约束;

  删除字段

  alter table 表明 drop 字段名;

  修改字段名称

   alter table 表名 change 旧字段名 新字段名 数据类型 约束;

  修改字段的数据类型

  alter table 表名 change 字段名 字段名 数据类型 约束;

  alter table 表名 modify 表名 数据类型 约束;

  修改表名

  alter table 旧表名 rename to 新表名;

3. 删除 drop

  删除数据库的命令
  DROP DATABASE db_test;
  删除数据表
  DROP TABLE tb_test;

DML

对表数据进行操作

  insert

  对所有的字段新增数据

  insert into 表名 values (数据1,数据2...数据n),(数据1,数据2...数据n);

  对指定的字段新增数据

  insert into 表名 (字段1,字段2...字段n) values (数据1,数据2...数据n);

  编码处理

  不能添加中文数据:需要更改mysql服务的编码格式。

  mysql的server: 默认的编码格式 latin  改成utf8

  mysql的核心配置文件: my.ini

  default-character-set=utf8

  character-set-server=utf8

  delete删除

  delete from 表名 [where 条件 ]; 清空表数据   多条记录受影响

  update更新

  update 表名 set 字段名1 = 值1,字段名2 = 值2 [where 条件];多条记录受影响

DQL语句select

基本语法

select 字段名 /*要查询的字段,多个字段用逗号隔开*/
from 表名 /*要查询的表名称*/
[where 筛选条件/*筛选记录的条件*/
group by 分组条件/*对结果进行分组*/
having 筛选条件 /*对分组后的记录进行条件筛选*/
order by 排序列 /*对结果进行排序*/
limit /*对记录总数进行限定*/]

select后面可以是表中的字段, 常量值, 表达式, 函数 ; 查询的结果是一个虚拟的表格

1, 基础查询 , 查询所有列

SELECT * FROM 表名;
-- 指定字段进行查询
SELECT 需要查询的列名查询多条以','隔开 FROM 表名;

 2, 条件查询

按条件表达式筛选  >, <, =, !=, <> (不等于), >=, <=

逻辑表达式 && || ! and, or, not

模糊查询 like, between 条件1 and 条件2 (not between and), in(列表中的值不支持通配符), is null(is not null)

  判断null使用is

CREATE TABLE t_stu(
    id INT,
    sname VARCHAR(10),
    sgender CHAR(1),
    age INT,
    score FLOAT(4,2),
    birthday TIMESTAMP,
        courseid INT,
    createtime date,
        updatetime date
)
建表语句
-- 指定某一个条件进行查询
SELECT * FROM t_stu WHERE courseid=2 
-- AND 是两个条件都要满足
SELECT * FROM t_stu WHERE courseid=2 AND score>60;
-- OR是条件只要满足一个就行
SELECT * FROM t_stu WHERE courseid=2 OR score>60;
-- 查询sid为 1,6,8的记录,属于某个集合
SELECT * FROM t_stu WHERE sid IN(1,6,8);
-- 查询sid不属于 1,6,8的记录
SELECT * FROM t_stu WHERE sid  NOT IN(1,6,8);
-- 查询记录某个字段为null 
SELECT * FROM t_stu WHERE sgender is null;
SELECT * FROM  tb_stu WHERE updatetime IS NOT null;
--  查询成绩在70到90区间范围内的记录
SELECT * FROM t_stu WHERE score BETWEEN 70 AND 90;
-- 性别非男的记录 <>和!= 都是 不等于
select * FROM t_stu WHERE sgender <> 'm';
select * FROM t_stu WHERE sgender != 'm';

 3. 模糊查询, 处理字符类型

% : 表示0个或多个字符

_ : 任意一个字符

-- 名字由3个字母组成
SELECT * FROM t_stu WHERE sname LIKE '___';
-- 名字由j 开头
SELECT * FROM t_stu WHERE sname LIKE 'j%';
-- 第二个字母为a 的学生记录
SELECT * FROM t_stu WHERE sname LIKE '_a%';
-- 查询姓名中包含字母a的记录
SELECT * FROM t_stu WHERE sname LIKE '%a%';

4. 去重查询&其别名

表或字段可以起别名 , 起别名便是利于理解 , 如果查询的字段有重名的情况使用别名可以区分 , AS可省略 使用空格

--  查询学生表中的所有性别
SELECT DISTINCT sgender FROM t_stu ;
-- 给查询出的字段起别名 AS是可以省略的 使用空格
SELECT age AS 年龄,sname AS 姓名 FROM  tb_stu;
SELECT sid a,sname b,sgender gender,score c FROM t_stu;

 5. 排序order by子句可以跟单个字段, 多个字段, 表达式, 函数, 别名

-- 查询所有学生记录,按成绩进行降序排序
-- 缺省是ASC升序
SELECT * FROM t_stu ORDER BY score DESC;
-- 查询所有学生记录,首先先按成绩进行降序排序,如果成绩相同,按名字进行升序排序
SELECT * FROM t_stu ORDER BY score DESC, sname ASC;

 6. 组函数/聚合函数/分组函数

用于统计使用, 又称为聚合函数或统计函数或组函数

聚合函数是用来纵向运算的函数

COUNT(字段) : 统计指定列不为NULL的记录行数; 一般使用COUNT(*)统计行数

MAX(字段) : 计算指定列的最大值 , 如果指定列是字符串类型 , 那么使用字符串排序运算

MIN(字段) : 计算指定列的最小值 , 如果指定列是字符串类型 , 那么使用字符串排序运算

SUM(字段) : 计算指定列的数值和 , 如果指定列类型不是数值类型 , 那么计算结果为0

AVG(字段) : 计算指定列的平均值 , 如果指定列类型不是数值类型 , 那么计算结果为0

sum,avg一般处理数值类型

max,min,count可以处理任意数据类型

分组函数忽略了null值 ,  可以和distinct搭配使用

注意 : 组函数可以出现多个 , 但是不能嵌套使用 , 如果没有group by字句 , 结果集中所有行数作为一组

CREATE TABLE DEPT
 (DEPTNO int(2) not null ,
    DNAME VARCHAR(14) ,
    LOC VARCHAR(13),
    primary key (DEPTNO)
    );
CREATE TABLE EMP
(EMPNO int(4)  not null ,
    ENAME VARCHAR(10),
    JOB VARCHAR(9),
    MGR INT(4),
    HIREDATE DATE  DEFAULT NULL,
    SAL DOUBLE(7,2),
    COMM DOUBLE(7,2),
    primary key (EMPNO),
    DEPTNO INT(2) 
    )
    ;

CREATE TABLE SALGRADE
( GRADE INT,
    LOSAL INT,
    HISAL INT );
建表语句
-- 查询emp表中有佣金的人数,统计指定列不为NULL的记录行数
SELECT count(comm) a FROM emp;
SELECT count(empno) 总人数 FROM emp;
SELECT count(*) 总人数 FROM emp;
SELECT count(1) 总人数 FROM emp;
-- 查询emp表中有佣金的人数
SELECT count(comm) from emp;
-- 查询emp表中月薪大于2500的人数:
SELECT * FROM emp WHERE sal>2500;
-- 统计月薪与佣金之和大于2500元的人数: ifnull(表达式1,表达式2)  如果表达式1为null那么取表达式2的值,否则取表达式1的值
SELECT * FROM emp WHERE sal+ifnull(comm,0) >2500;
查询有佣金的人数,以及有领导的人数:
SELECT count(comm) FROM emp WHERE mgr is not null;
-- 查询所有雇员月薪和:
SELECT sum(sal) 总薪资  FROM emp;
-- 查询所有雇员月薪和,以及所有雇员佣金和:
SELECT sum(sal) 总薪资,SUM(comm) 总佣金 FROM emp;
-- 查询所有雇员月薪+佣金和
SELECT sum(sal+ifnull(comm,0)) 总佣金 FROM emp;
SELECT sum(sal)+sum(ifnull(comm,0)) 总佣金 FROM emp;
-- 统计所有员工平均工资
SELECT avg(sal) 平均薪资 FROM emp;
-- 查询最高工资和最低工资
SELECT max(sal),min(sal) FROM emp;

7.group by分组查询

查询出来的字段要求是group by后的字段 , 查询字段中可以出现组函数

group by后面可以跟聚合函数 , 可以其别名

--     查询每个部门的部门编号和每个部门的工资和
SELECT deptno,SUM(sal) FROM emp GROUP BY deptno;
--     查询每个部门的部门编号以及每个部门的人数
SELECT deptno,SUM(sal),COUNT(1) FROM emp GROUP BY deptno;
--     查询每个部门的部门编号以及每个部门员工工资大于1500的人数:
SELECT deptno,COUNT(1) FROM emp WHERE sal>1500 GROUP BY deptno;

按多个字段分组, 后面字段一致的为一组

-- 按job进行分类
SELECT COUNT(*),job FROM emp GROUP BY job;
-- 按job和mgr进行分类
SELECT COUNT(*),job,mgr FROM emp GROUP BY job,mgr;

8, having子句

where是对分组进行过滤; having是对分组后进行过滤

where中不能出现分组/聚合函数, having中可以出现

where是比分组先执行的 , having是在分组后执行的

having后面可以跟别名

-- 查询工资总和大于9000的部门编号以及工资和:
SELECT deptno,sum(sal)  FROM emp GROUP BY deptno HAVING sum(sal)>9000;
-- having中使用别名
SELECT deptno,sum(sal) 总薪资 FROM emp GROUP BY deptno HAVING 总薪资>9000;
-- 查询部门员工个数大于3的,having中使用了别名
SELECT COUNT(1) cc,deptno FROM emp GROUP BY deptno HAVING cc>3;

 9, limit

-- 第一位表示起始索引位置,第二位表示总的长度;在分页中会使用
SELECT * FROM emp LIMIT 1,5;
SELECT * FROM emp LIMIT 5; 等价与 SELECT * FROM emp LIMIT 0, 5;

10, 多表查询, 关联查询

内连接

  • 多表等值连接的结果是多表的交集部分, N表连接, 至少需要N-1个连接条件, 没有顺序需求, 一般起别名
  • 非等值连接, 只要不是等号连接的都是非等值连接

外连接 , 有主表有从表 , 主表肯定会显示完整的内容

  • 左外连接 , 以左表为主
  • 右外连接 , 以右表为主
-- 查询员工信息,要求显示员工号,姓名,月薪,部门名称
-- 笛卡尔积 (a, b) (1,2,3) --(a,1) (a,2) (a,3) (b,1) (b,2) (b,3)--》会生成一个中间表
-- 多表查询,关联条件使用的是等号
-- 查询员工信息,要求显示员工号,姓名,月薪,部门名称  等值连接
SELECT empno,ename,sal,emp.deptno,dname FROM emp,dept WHERE emp.deptno=dept.deptno;
-- 给表起别名
SELECT empno,ename,sal,a.deptno,dname FROM emp a,dept b WHERE a.deptno=b.deptno;
-- 非等值连接
-- 查询员工信息,要求显示:员工号,姓名,月薪,薪水的级别
SELECT empno,ename,sal,GRADE FROM emp e,salgrade sa WHERE e.sal BETWEEN sa.LowSAL AND sa.HISAL; 

-- 外连接
-- 查询员工信息,要求显示员工号,姓名,月薪,部门名称 使用内连接和等值连接等同
SELECT empno,ename,sal,emp.deptno,dname FROM emp INNER JOIN dept on emp.deptno=dept.deptno;
-- 左外连接 左边的表内容全部显示,右边的表没有的以null进行填充
SELECT empno,ename,sal,emp.deptno,dname FROM emp LEFT JOIN dept on emp.deptno=dept.deptno;
-- 右外连接,右表内容全部显示,左表没有的以null进行填充
SELECT empno,ename,sal,emp.deptno,dname FROM emp RIGHT JOIN dept on emp.deptno=dept.deptno;

 ON后面的条件(ON条件)和WHERE条件的区别

  ON条件 : 是过滤两个链接表笛卡尔积形成中间表的约束条件

  WHERE条件 : 在没有ON的单表查询中 , 是限制物理表或者中间查询结果返回记录的约束 , 在两表或多表连接中是限制连接形成最终中间表的返回结果的约束

  建议 : ON只进行连接操作 , WHERE只过滤中间表的记录

11, 自连接 , 通过别名 , 将同一张表示为多张表 ; 同一张表中某个字段要去关联另一个字段

-- 查询员工姓名和员工的老板的名称
SELECT e1.empno,e1.ename,e2.empno,e2.ename  FROM emp e1, emp e2 WHERE e1.mgr=e2.empno;

 12, 子查询

以一个查询的结果为另一个查询的条件

-- 查询工资为20号部门平均工资的员工信息
SELECT * FROM emp WHERE sal>(SELECT AVG(sal) FROM emp GROUP BY deptno HAVING deptno=20);

 约束

约束是为了表的数据的正确性 , 如果数据不正确 , 那么一开始就不能添加到表中

非空约束(not null)

指定非空约束的列不能没有值 , 也就是说在插入记录时 , 对添加了非空约束的列一定要给值 ; 在修改记录时 , 不能把非空列的值设置为NULL

CREATE TABLE a(
    id INT NOT NULL,
    sname VARCHAR(20)
)
    
 --  提示错误:Field 'id' doesn't have a default value

唯一性约束(nuique)

当为字段指定唯一性约束后 , 那么字段的值必须唯一(null值除外)

CREATE TABLE b(
    id INT NOT NULL UNIQUE
    )

默认约束(default)

当字段指定默认约束后 , 给字段一个默认值

CREATE TABLE c(
    id INT NOT NULL DEFAULT 6,
    sname VARCHAR(3)
    )

主键约束(primary key)

当某一咧添加了主键约束后 , 那么这一列的数据就不能重复出现 , 这样每行记录中其主键列的值就是这一行的唯一表示 , 主键列的值不能为NULL , 也不能重复

任意类型的字段都可以充当主键 , 一个表有且只有一个主键

如果主键列是整数类型 , 一般不会手动赋值 , 都是结合mysql的sever自动维护主键列的数据 (自增特性 , 主键自增长 , auto_increment)

create table d(
 id int primary key auto_increment,
 name varchar(20) not null UNIQUE,
 age tinyint(2)
);
insert into d (name,age) values ('张三',20);
修改自增的初始值:
alter table 表名 auto_increment = 初始值;

如果主键约束是字符串类型 , 一般不会手动给值 , 都是结合mysql的sever自动维护主键列的数据

create table a(
 id varchar(64) primary key ,
 name varchar(20) not null UNIQUE,
 age tinyint(2)
);
insert into a (id,name,age) values (uuid(),'张三',20);

外键约束

  • 主外键是构成表与表关联的唯一途径
  • 外键代表着另一张表的主键 , 外键的值必须从另一张表的主键中选择
  • 外键是另一张表的主键 , 例如员工表与部门表之间存在关联关系 , 其中员工表中的部门编号字段就是外键 , 是相对部门表的外键
create table tb_type(
 id int primary key auto_increment,
 typename varchar(20) not null unique,
 desc varchar(100),
 createtime datetime,
 updatetime datetime
);
create table tb_good(
 id int primary key auto_increment,
 gname  varchar(20) not null unique,
 price float(10,2) not null,
 createtime datetime,
 updatetime datetime,
 );

需要将商品表与类型表进行一对一的关联关系

商品表(从表/子表)typeid的数据要严格参照类型表(主表)主键id的数据

Alter table tb_good add typeid int(2);
alter table tb_good add CONSTRAINT  fk_typeid  
FOREIGN key (typeid ) REFERENCES tb_type (id);

RESTRICT: 删除,修改都是受限。
CASCADE: 级联操作。 (不能使用)
SET NULL: 前提:外键列可以为null。(可以使用)
外键不推荐使用,弱化外键。(所有的关联都在逻辑层面进行处理)

常用函数

注意 : MySQL中的'+'就只有运算符的功能 ; 会试图将字符型数值转换成数值型在继续操作 , 转换失败则转为0 ; 若其中有Null则结果为null ; 字符串可以使用concat函数进行拼接

字符串函数:

length(str)  得到的是自己而个数utf8的中文是3个字节

char_length(str)  返回字符串str的长度

insert(str1,pos,len.newstr)  字符串str从第pos位置开始的len个字符替换成新自负newstr

ipad(str,len,padstr)  返回字符串str , 其左边由字符串padstr填补到len字符串长度

rpad(str,len,padstr)  返回字符串str , 其右边由字符串padstr填补到len字符串长度 

concat(str1,str2)   拼接字符串

upper/lower(str)  转为大/小写

substring(str,pos,len)  从字符串str的pos位置起len个字符长度子串

instr(str,substr)  返回字符串第一次出现的索引 , 找不到则为0

trim(str)  去除字符串头尾空格

repeat(str,cont)  返回str重复count次的结果

replace(str,from_str,to_str)  用字符串to_str替换字符串str中所有的字符串from_str

-- 字符串连接
SELECT concat('java','sun','aa');
-- length()
SELECT length(sname),sname FROM tb_stu;
-- 指定位置插入
SELECT INSERT('javasun',5,3,'oracle');
-- 字符长度
SELECT CHAR_LENGTH('java');
-- 左填充
SELECT lpad('java',9,'bb');
-- 右填充
SELECT rpad('java',10,'c');
-- 去除前后空格
SELECT trim('  ja  va    ');
-- 重复指定次数
SELECT repeat('ja',4);
-- 字符串替换
SELECT REPLACE('javaoror','or','sun');
-- 截取字串
SELECT substring('javasun',5,3);

数值函数:

ABS(X)  返回x的绝对值

CEIL(X)  返回不小于x的最大整数

FLOOR(X)  返回不大于x的最大整数

MON(X,Y)  返回x/y的模

RAND()  返回一个0-1之间的随机浮点数

ROUND(X,Y)  返回数字x的四舍五入的有Y位小数的值

TREUNCATE(X,Y)  返回数字x截断位y位小数的结果

MOD(X,Y)  取x除以y的余数

-- 取绝对值
SELECT abs(-32);
-- 向上取最小整数
SELECT ceil(3.2);
-- 向下取最大整数
SELECT floor(3.2);
SELECT floor(score),score FROM tb_stu;
-- 取余数
SELECT mod(21,3);
-- 得到 0-1直接的随机值
SELECT rand();
-- 有2位小数的四舍五入值
SELECT round(5.678,2);
-- 截断,小数位保持2位
SELECT TRUNCATE(5.67888,2);

日期函数:

CURDATE()  返回当前日期 , 不包含时间

CURTIME()  返回当前时间 , 不包含日期

NOW()  返回当前的时间和日期

WEEK(date)  返回指定日期为一年中的第几周

YEAR(date)  获取指定的年份

HOUR(time)  返回time的小时制

MINUTE(time)  返回time的分钟值

MONTHNAME(date)  返回date的月份

DATEDIFF(expr,expr2)  返回起始时间expr和结束时间expr2之间的天数

DATE_FORMATE(date,fmt)  返回按字符串fmt格式化日期date值

from_unixtime(unix_timestamp,"%Y-%m-%d %H:%i:%S")  常用来将毫秒值转换为时间格式

%M 月名(January---December) %b 缩写的月份名(Jan---Dec) %s 秒(00-59)
%W 星期名(Sunday---Saturday) %j 一年中的天数(001---366) %S 秒(00-59)
%D 有英语前缀的月份的日期(1st,2nd,3rd...) %H 小时(00---23) %p AM或PM
%Y 年 , 数字 , 4位 %k 小时(0---23) %w 一个星期中的天数(0=Sunday---6=Saturday)
%y 年 , 数字 , 2位 %h 小时(01---12) %U 星期(0---52)这里星期天是星期的第一天
%a 缩写的星期名(Sun---Sat) %I 小时(1---12) %u 星期(0---52) 这里星期一是星期的第一天
%d 月份中的天数 , 数字(00---31) %i 分钟 , 数字(00---59) %% 一个文字"%"
%e 月份中的天数 , 数字(0---31) %r 时间 , 12小时(hh:mm:ss[AP]M)  
%m 月 , 数字(01-12) %T 时间 , 24小时(hh:mm:ss)  
%c 月 , 数字(1---12) %S 秒(00---59)  

 

 

-- 当前日期,当前时间,日期和时间
SELECT CURDATE(),CURTIME(),now();
-- 指定日期是一年中的第几周
SELECT WEEK(now());
-- 返回指定日期的年份
SELECT YEAR(now());
-- 返回指定时间的小时
SELECT hour(now()),hour(CURTIME());
-- 返回date的月份名
SELECT MONTHNAME(now());
-- 计算时间相隔的天数
SELECT DATEDIFF('2021-1-15','2021-1-12');
SELECT DATEDIFF(now(),'2021-1-10');
-- 把日期按指定的格式进行转换
SELECT DATE_FORMAT(now(),'%Y-%m-%d %H:%i:%s');
-- 把字符串转日期
SELECT STR_TO_DATE('2021-1-12','%Y-%m-%d');
-- 将毫秒数转换为时间格式
SELECT FROM_UNIXTIME(646545353);

其他函数

verson() 版本号

user() 当前用户

 if(exp1,exp2,exp3)如果exp1为ture取exp2的值否则取exp3的值

SELECT VERSION();
SELECT USER();
SELECT DATABASE();
SELECT IF(10>2,'10','2') a;

 数据库范式

第一范式(1NF)用来确保每列的原子性 , 要求每列(或者每个属性)都是不可再分的最小数据单位(也成为最小的原子单位)

第二范式(2NF)在第一范围的基础上更近一层 , 要求表中的每列都和主键有关 , 即要求试题的唯一性 , 如果一个表满足第一范式 , 并且除了主键意外的其他列全部都依赖于该主键 , 那么该表满足第二范式

第三范式(3NF)在第二范式的基础上更近一层 , 第三范式是确保每列都和主键列直接相关 , 而不是间接相关 , 即限制列的冗余性 , 如果一个关系满足第二范式 , 并且除了主键以外的其他列都依赖于主键列 , 列和列间不存在相互依赖关系 , 则满足第三范式

数据库存储引擎 : 在MySQL中的数据使用不同的存储技术存储在文件或内存中

-- 查看MySQL中所用的存储引擎
show engines;

 

 innodb是支持事物的

myisam , memory不支持事务

数据库事务

什么是事务

  • Transaction
  • 事务,只是一个操作的集合,是一个程序的执行单元
  • 事务 : 一个最小的不可再分的工作单位 ; 通常一个事务对应一个完成的业务(例如银行账户转账业务 , 该业务就是一个最小的工作单位)
  • 一个完成的业务需要批量的DML(insert , update , delete)语句共同联合完成
  • 事务只和DML语句相关 , 或者说DML语句才有事务 , 这个和业务逻辑有关 , 业务逻辑不同 , DML语句的个数不同

事务死他特性ACID

  • 原子性(Atonicity) : 事务是不可分割的最小工作单位 , 业务内的操作要么做 , 要么全不做 , 事务具有要么全部成功 , 要么全部失败 
  • 一致性(Consistency) : 在事务执行前和事务执行完成后 , 数据库的数据依然处于正确状态 , 即数据完整性约束没有被破坏 , 如A给B转账 , 不论转账是否成功A和B的账户总额和转账之前是相同的
  • 各理性(Isonlation) : 当多个事务处于并发访问同一个数据库资源时 , 事务之间互相影响 , 不同的隔离级别决定了各个事务对数据资源访问的不同行为
  • 持久性(Durability) : 事务一旦执行成功 , 它对数据库的改变是不可逆的

在业务逻辑层 , 一个service层可能需要执行一次或多次增删改操作 , 如果着期间发生了异常 , 数据库事物如果不回滚数据库中的数据就会不完整 , 例如 : 订单信息中包含了订单明细 , 现在在保存订单的业务逻辑方法中 , 现保存订单成功了 , 在保存订单明细 , 如果保存订单明细的过程中失败了 , 可定希望之前的保存订单数据回滚

和事务相关的语句

  • 开启事务 : start transaction | begin
  • commit : 提交
  • rollback : 回滚

事务何时开启何时结束

开始表示 : Mysql默认情况下任何一条DML语句(insert update delete)执行 , 标志事务的开启

结束标志 : 

  • 提交 : 成功的结束 , 将所有的DML语句操作历史记录和底层数据来一次同步
  • 回滚 : 失败的结束 , 将所有的DML语句操作历史记录全部清空

事务和底层数据库的关系

  在事务进行的过程中 , 为结束之前 , DML语句是不会更改底层数据 , 只是将历史操作记录一下 , 在内存中完成记录 , 只有在事务结束的时候 , 而且是成功结束的时候 , 才会修改底层硬盘文件中的数据 , 如果执行失败 , 则清空内存中所有的历史操作记录 , 不会对底层文件中(数据表)数据作任何操作

-- 开启事务
START TRANSACTION;
INSERT into tb_stu(sid,sname) VALUES(5,'jerry'); 
-- rollback 的执行不会更新 磁盘文件
-- commit是事务执行成功 提交 
COMMIT;

 

 在MySQL中事务的提交和回滚

  在MySQL中 , 默认情况下 , 事务是自动提交的 , 也就是说 , 只要执行一条DML预计就开启了事务 , 并且提交了事务 , 自动提交机制是可以关闭的 , 可以有程序控制何时提交

事务的隔离级别

并发情况下事务引发的问题

一般情况下 , 多个单元操作并发执行 , 会出现这么几个问题

  • 脏读 : A事务还未提交 , B事务就读到了A事务的结果(破坏了隔离性)
  • 不可重复读 : A事务在本次事务中 , 对自己未操作的事务进行了多次读取 , 结果出现了不一致或记录不存在的情况(破坏了一致性 , 特性是update , delete)
  • 幻读 : A事务在本次事务中 , 对自己未操作的事务进行了多次读取 , 第一次读取时记录不存在 , 第二次读取时记录出现了(破坏了一致性 , insert)

不同事务之间具有隔离性 , 隔离级别分四个 :

为了权衡 ' 隔离 ' 和 ' 并发 ' 的矛盾 , ISO定义了四个事务的隔离级别 , 每个级别的隔离程度不同 , 允许出现的副作用也不同

  • 读未提交 : read uncommitted(读到未提交的数据) 最低一级别 , 只能保证持久性
  • 读已提交 : read committde(读到以提交的数据) 语句级别
  • 可重复读 : repeatable read 事务级别
  • 串行化 : serializable 最高级别 , 事务与事务完全串行化执行 , 毫无并发可言 , 性能较低
show variables like 'transaction_isolation';
事务隔离级别 脏读 不可重复读 幻读
读未提交 : read uncommitted
读已提交 : read committde -
可重复读 : repeatable read - -
串行化 : serializable 最高级别  - - -
posted @ 2021-01-01 00:00  小_Leo  阅读(152)  评论(0编辑  收藏  举报