MySQL高级应用

MySQL入门:https://blog.csdn.net/m0_60121089/article/details/125521332


目录

八、存储过程

8.1概述

8.1.1SQL指令执行过程

8.1.2存储过程简介

8.1.3存储过程优缺点

8.2创建存储过程

8.3调用存储过程

8.4变量

8.4.1局部变量​​​​​​​

8.4.2用户变量

8.4.3给变量赋值

8.4.4将查询结果赋给变量

8.5存储过程的参数

8.5.1in输入参数

8.5.2out输出参数

8.5.3inout输入输出参数

8.6流程控制

8.6.1分支语句

8.6.2循环语句

8.7存储过程管理

8.7.1查询存储过程

8.7.2修改存储过程

8.7.3删除存储过程

8.8综合案例

8.8.1创建表、添加数据

8.8.2业务分析

8.8.3创建存储过程

8.8.4调用存储过程

8.9游标

8.9.1概念

8.9.2使用步骤

九、触发器

9.1概述

9.2触发器的使用

9.2.1创建触发器

9.2.2查看触发器

9.2.3测试触发器

9.2.4删除触发器

9.3NEW与OLD

9.4触发器使用总结

9.4.1优点

9.4.2缺点

9.4.3存储过程和触发器使用建议

十、视图

10.1概述

10.2创建视图

10.3查询视图结构、修改视图、删除视图

十一、索引

11.1概述

11.2创建索引

11.2.1唯一索引

 11.2.2普通索引

11.2.3组合索引

11.2.4全文索引

11.3索引使用

11.4查看索引

11.5删除索引

11.6索引的使用总结

十二、事务

12.1概述

12.1.1什么是事务

12.1.2事务特性(ACID)

12.2事务管理

12.3事务隔离级别

12.3.1读未提交(read uncommitted)

12.3.2读已提交(read committed)

12.3.3可重复读(repeatable read)

12.3.4串行化(serializable)

12.3.5设置数据库事务隔离级别

十三、数据库设计

13.1数据库设计流程

13.2数据库设计三范式

13.2.1第一范式

13.2.2第二范式

13.2.3第三范式

13.3数据库建模

13.3.1E-R图

13.3.2三线图

13.3.3PowerDesigner(PD) 

13.3.4PDMan


八、存储过程

8.1概述

8.1.1SQL指令执行过程

 存在的问题:

  • 如果重复执行相同的SQL,那么SQL引擎会重复编译并执行这些SQL。
  • 如果连续执行多个SQL,并且第二个SQL指令需要使用第一个SQL指令执行的结果作为参数,那么就不太方便。

8.1.2存储过程简介

将能够完成特定功能的SQL指令进行封装(指令集),编译之后存储在服务器上,并且为之取一个名字。客户端可以通过名字调用指令集,获取执行结果。(类似于编程语言中的函数)。

8.1.3存储过程优缺点

优点:

  • SQL指令⽆需客户端编写,通过⽹络传送,可以节省⽹络开销,同时避免SQL指令在⽹络 传输过程中被恶意篡改保证安全性
  • 存储过程经过编译创建并保存在数据库中的,执⾏过程⽆需重复的进⾏编译操作,对SQL 指令的执⾏过程进⾏了性能提升;
  • 存储过程中多个SQL指令之间存在逻辑关系,⽀持流程控制语句(分⽀、循环),可以实现更为复杂的业务;

缺点:

  • 存储过程是根据不同的数据库进⾏编译、创建并存储在数据库中;当我们需要切换到其他的数据库产品时,需要重写编写针对于新数据库的存储过程;
  • 存储过程受限于数据库产品,如果需要⾼性能的优化会成为⼀个问题;
  • 在互联⽹项⽬中,如果需要数据库的⾼(连接)并发访问,使⽤存储过程会增加数据库的连接执⾏时间(因为我们将复杂的业务交给了数据库进⾏处理,而不是程序处理)。

8.2创建存储过程

Java语法中,方法可以有有参数和返回值的;存储过程中,可以有输入参数和输出参数。

语法

create procedure <proc_name>([IN/OUT args])
begin
 -- SQL
end;

实例

#创建一个存储过程实现两个整数的加法
create procedure proc1(in a int,in b int,out c int)
begin
	set c = a+b;
end;

8.3调用存储过程

#定义全局变量@m
set @m = 0;
#调用存储过程(2传给a,3传给b,@m传给c)
call proc1(2,3,@m);
#显示变量@m的值
select @m from dual;

8.4变量

8.4.1局部变量

局部变量:定义在存储过程中的变量,只能在存储过程内部使用。

语法

-- 局部变量要定义在存储过程中,⽽且必须定义在存储过程开始
declare <attr_name> <type> [default value];

实例

#创建一个存储过程实现一个数的平方与这个数的一半相加
create procedure proc2(in a int,out b int)
begin
	declare x int;#定义x int类型,默认值为0
	declare y int default 1;#定义y,值为1
	set x = a*a;
	set y = a/2;
	set b = x+y;
end;

8.4.2用户变量

用户变量相当于全局变量。定义的用户变量存储在mysql数据库的数据字典中(dual),其值可以通过select @attrName from dual进行查询。

语法

set @attrName = value;

实例

set @n = 1;
select @n from dual;#1

注意

因为⽤户变量相当于全局变量,可以在SQL指令以及多个存储过程中共享,在开发中建议尽量少使⽤⽤户变量,⽤户变量过多会导致程序不易理解、难以维护。

8.4.3给变量赋值

语法

给变量(不管是全局变量还是局部变量)赋值的语法与定义全局变量的语法相同。

set @attrName = value;

实例

#修改全局变量@n的值
set @n = 2;
select @n from dual;#2

8.4.4将查询结果赋给变量

在存储过程中使用select ...into ...给变量赋值

实例

#创建一个存储过程实现查询学生表中学生的数量
create procedure proc3(out c int)
begin
	select count(stu_num) into c from student;#将查询结果赋给输出变量c
end;

#调用存储过程
call proc3(@n);
select @n from dual;

8.5存储过程的参数

8.5.1in输入参数

存放调用存储过程时传递过来的具体数据的参数。

#创建存储过程实现添加学生信息
create procedure proc4
(
	in snum char(8),
	in sname varchar(20),
	in sgender char(2),
	in sage int,
	in classid int
)
begin
	insert into student
	values(snum,sname,sgender,sage,classid);
end;

#调用存储过程
call proc4('20210108','吴八','男',20,3);

8.5.2out输出参数

将存储过程中产生的数据返回给存储过程调用者(相当于Java方法的返回值,不同之处在于一个存储过程可以有多个输出参数)。

#创建存储过程实现根据学号查询学生姓名
create procedure proc5(in snum char(8),out sname varchar(20))
begin
	select stu_name into sname 
	from student 
	where stu_num = snum;
end;

set @name = '';
call proc5('20210108',@name);
select @name from dual;

8.5.3inout输入输出参数

既是in输入参数,又是out输出参数。

#创建存储过程实现根据学号查询学生姓名(inout参数实现)
create procedure proc6(inout str varchar(20))
begin
	select stu_name into str
	from student
	where stu_num = str;
end;

set @name = '20210108';
call proc6(@name);
select @name from dual; 

8.6流程控制

8.6.1分支语句

单分支

#创建存储过程实现以下功能:
#如果输入参数值为1,则添加一条班级信息
create procedure proc7(in a int)
begin
	if a=1 then
		insert into class(class_name,class_remark) 
		values('Java2107','hhh');
	end if;
end;

双分支

#创建存储过程实现以下功能:
#如果输入参数值为1,则添加一条班级信息;如果输入参数值为其他,则添加一条学生信息
create procedure proc8(in a int)
begin
	if a=1 then
		insert into class(class_name,class_remark) 
		values('Python2106','eee');
	else
		insert into student
		values('20210109','小刚','男',21,3);
	end if;
end;

case

#创建存储过程实现以下功能:
#如果输入参数值为1,则添加一条班级信息;如果输入参数值为2,则添加一条学生信息;如果输入参数值为其他,则提示输入错误
create procedure proc9(in a int)
begin
	case a
	when 1 then
		insert into class(class_name,class_remark) 
		values('Python2107','yyy');
	when 2 then
		insert into student
		values('20210110','小花','女',21,3);
	else
		select '输入错误!';
	end case;
end;

8.6.2循环语句

while

#创建存储过程并调用存储过程实现以下功能:
#循环添加3条班级信息(班级名称依次为'Java0'、'Java1'、'Java2')
create procedure proc10(in num int)
begin
	declare i int;
	set i = 0;
	while i < num do
		insert into class(class_name,class_remark)
		values(concat('Java',i),'abc');
		set i = i+1;
	end while;
end;

call proc10(3);

repeat

#创建存储过程并调用存储过程实现以下功能:
#循环添加3条班级信息(班级名称依次为'Python0'、'Python1'、'Python2'
create procedure proc11(in num int)
begin
	declare i int;
	set i = 0;
	repeat
		insert into class(class_name,class_remark)
		values(concat('Python',i),'abc');
		set i = i+1;
		until i >= num
	end repeat;
end;
call proc11(3);

loop

#创建存储过程并调用存储过程实现以下功能:
#循环添加3条班级信息(班级名称依次为'C++0'、'C++1'、'C++2'
create procedure proc12(in num int)
begin
	declare i int;
	set i = 0;
	myloop:loop
		insert into class(class_name,class_remark)
		values(concat('C++',i),'abc');
		set i = i+1;
		if i = num then
			leave myloop;
		end if;
	end loop;
end;
call proc12(3);

8.7存储过程管理

  • 存储过程是属于某个数据库的,也就是说当我们将存储过程创建在某个数据库之后,只能在当前数据库中调⽤此存储过程。

8.7.1查询存储过程

查询某个数据库中有哪些存储过程。
#根据数据库名,查询当前数据库中的存储过程
show procedure status where db = 'test2';

#查询存储过程的创建细节
show create procedure test2.proc1;

8.7.2修改存储过程

修改存储过程的特征/特性

#修改存储过程的特征参数为not sql
alter procedure proc1 contains sql;
存储过程的特征参数:
  • not sql        表示子程序中不包含sql语句
  • contains sql        表示子程序包含sql语句,但不包含读或写数据的语句
  • reads sql data        表示子程序中包含读数据的语句
  • modifes sql data        表示子程序包含写数据的语句
  • sql security{definer | invoker}        指明谁有权限来执行
    • definer        表示只有定义者自己才能够执行
    •  invoker        表示调用者可以执行
  • comment 'string'        表示注释信息        

8.7.3删除存储过程

#删除存储过程proc1
#drop 删除数据库中的对象(数据库、数据表、列、存储过程、触发器、视图、索引)
#delete 删除数据表中的数据
drop procedure proc1;

8.8综合案例

使用存储过程完成借书操作

8.8.1创建表、添加数据

学生表

-- 创建学生信息表
create table student(
 stu_num char(4) primary key,
 stu_name varchar(20) not null,
 stu_gender char(2) not null,
 stu_age int not null
);
-- 添加学生信息
insert into student(stu_num,stu_name,stu_gender,stu_age)
values('1001','张三','男',20);
insert into student(stu_num,stu_name,stu_gender,stu_age)
values('1002','李四','女',20);
insert into student(stu_num,stu_name,stu_gender,stu_age)
values('1003','王五','男',20);

图书表

-- 创建图书信息表
create table book(
 book_id int primary key auto_increment,
 book_name varchar(50) not null,
 book_author varchar(20) not null,
 book_price decimal(10,2) not null,
 book_stock int not null,
 book_desc varchar(200) );
 
 -- 添加图书信息
insert into
book(book_name,book_author,book_price,book_stock,book_desc)
values('Java程序设计','亮亮',38.80,12,'亮亮老师带你学Java');
insert into
book(book_name,book_author,book_price,book_stock,book_desc)
values('Java王者之路','威哥',44.40,9,'千锋威哥,Java王者领路人');

借书记录表

-- 创建借书记录表
create table record(
	rid int primary key auto_increment,
	stu_num char(4) not null,
	book_id int not null,
	borrow_num int not null,
	is_return int not null,-- 0表示未归还,1表示已归还
	borrow_date date not null,
	constraint FK_RECORD_STUDENT foreign key(stu_num) references student(stu_num),
	constraint FK_RECORD_BOOK foreign key(book_id) references book(book_id)
);

8.8.2业务分析

判断:学生是否存在、图书是否存在、图书库存是否充足。

操作:如果以上三者判段结果都为真,则修改图书库存、添加借书记录。

8.8.3创建存储过程

-- 创建存储过程实现借书业务
-- 输入参数snum	学号
-- 输入参数bid	图书编号
-- 输入参数bnum	借书数量
-- 输出参数state	借书状态(借书成功,学号不存在,图书不存在,图书库存不足)

create procedure proc_borrow_book
(
	in snum char(4),
	in bid int,
	in bnum int,
	out state varchar(20)
)
begin
	declare stu_count int;
	declare book_count int;
	declare stock int;
	-- 判断学生是否存在
	select count(*) into stu_count from student where stu_num = snum;
	if stu_count > 0 then	-- 如果学生存在
		-- 判断图书是否存在
		select count(*) into book_count from book where book_id = bid;
		if book_count > 0 then	-- 如果图书存在
			-- 判断图书库存是否充足
			select book_stock into stock from book where book_id = bid;
			if stock >= bnum then	-- 如果库存数量大于等于借书数量
				update book set book_stock = stock - bnum where book_id = bid;-- 库存数量修改
				insert into record(stu_num,book_id,borrow_num,is_return,borrow_date)
				values(snum,bid,bnum,0,sysdate());-- 向借书记录表中插入一条数据
				set state = '借书成功';
			else	-- 如果库存数量小于借书数量
				set state = '图书库存数量不足';
			end if;
		else	-- 如果图书不存在
			set state = '图书不存在';
		end if;
	else	-- 如果学生不存在
		set state = '学号不存在';
	end if;
end;

8.8.4调用存储过程

-- 测试学号不存在
set @state = '';
call proc_borrow_book('1005',2,5,@state);
select @state as '借书状态' from dual;

-- 测试图书不存在
call proc_borrow_book('1002',3,5,@state);
select @state as '借书状态' from dual;

-- 测试图书库存数量不足
call proc_borrow_book('1002',2,10,@state);
select @state as '借书状态' from dual;

-- 测试借书成功
call proc_borrow_book('1002',2,2,@state);
select @state as '借书状态' from dual;

 查看借书操作是否成功:

第二本书的库存数量由4本变为2本 。

 

 借书记录也成功添加到record表中。

8.9游标

应用场景:调用一个存储过程,一次返回表中的多条记录,需要用到游标。

8.9.1概念

游标是用来依次取出查询结果集中的每一条数据(逐条读取查询结果集中的记录),相当于Java容器中的迭代器。

8.9.2使用步骤

语法

1.声明游标

DECLARE cursor_name CURSOR FOR select语句;

 2.打开游标

OPEN cursor_name;

3.使用游标(提取游标当前指向的记录

FETCH mycursor INTO attrName1,attrName2,...;

注:提取完成后,游标自动下移。

4.关闭游标

CLOSE cursor_name;

实例

创建存储过程

-- 创建存储过程返回图书表中所有记录的图书名称和图书价格
create procedure proc1(out result varchar(40))
begin
	declare i int;
	declare num int;
	declare bname varchar(20);
	declare bprice decimal(5,2);
	declare str varchar(20);
	-- 声明游标
	declare mycursor cursor for select book_name,book_price from book;
	-- 打开游标
	open mycursor;
	-- 将图书表中的记录数赋给num
	select count(*) into num from book;
	-- 使用游标需要结合循环
	set i = 0;
	while i < num do
		-- 使用游标,提取图书表中每一条记录
		fetch mycursor into bname,bprice;
		-- 用‘~’将每条记录的名称和价格拼接起来
		set str = concat_ws('~',bname,bprice);-- select concat_ws('~',bname,bprice) into str;
		-- 用‘,’将每个str字符串拼接起来
		set result = concat_ws(',',result,str);
		set i = i+1;
	end while;
	-- 关闭游标
	close mycursor;
end;

调用存储过程

set @result = '';
call proc1(@result);
select @result from dual;

九、触发器

9.1概述

触发器,就是⼀种特殊的存储过程。触发器和存储过程⼀样是⼀个能够完成特定功能、存储在数据库服务器上的SQL ⽚段。但是当对数据表中的数据执⾏ DML操作 时,⾃动触发这个SQL ⽚段的执⾏,⽆需⼿动调⽤。

9.2触发器的使用

创建学生信息操作日志表

create table stulog(
	log_id int primary key auto_increment,
	log_time timestamp,
	log_text varchar(100)
);

9.2.1创建触发器

语法

create trigger tri_name
<before|after> -- 定义触发时机
<insert|delete|update> -- 定义DML类型
ON <table_name>
for each row -- 声明为⾏级触发器(只要操作⼀条记录就触发触发器执⾏⼀次)
sql_statement -- 触发器操作

实例

-- 创建触发器:当学⽣信息表发⽣添加操作时,则自动向⽇志信息表中添加⼀条⽇志
create trigger tri1
after insert
on student
for each row
insert into stulog(log_time,log_text)
values(now(),concat('添加了',NEW.stu_num,'学生信息'));

9.2.2查看触发器

show triggers;

9.2.3测试触发器

-- 触发一次触发器
insert into student
value('1005','小明','男',20);

-- 触发两次触发器
insert into student
value('1006','小刚','男',21),('1007','小花','女',19);

9.2.4删除触发器

drop trigger tri1;

9.3NEW与OLD

触发器⽤于监听对数据表中数据的insert、delete、update操作,在触发器中通常处理⼀些DML的关联操作;我们可以使⽤ NEW 和 OLD 关键字在触发器中 获取触发这个触发器的DML操作的数据
  • NEW : 在触发器中⽤于获取insert操作添加的数据、update操作修改后的数据。
  • OLD:在触发器中⽤于获取delete操作删除的数据、update操作修改前的数据。

insert上的触发器

-- NEW表示添加的数据
create trigger tri1
after insert
on student
for each row
insert into stulog(log_time,log_text)
values(now(),concat('添加了',NEW.stu_num,'学生信息'));

delete上的触发器

-- OLD表示删除的数据
create trigger tri2
after delete
on student
for each row
insert into stulog(log_time,log_text)
values(now(),concat('删除了',OLD.stu_num,'学生信息'));

update上的触发器

-- OLD表示修改前的数据,NEW表示修改后的数据
create trigger tri3
after update
on student
for each row
insert into stulog(log_time,log_text)
values(now(),concat('学生学号由',OLD.stu_num,'修改为',NEW.stu_num));

9.4触发器使用总结

9.4.1优点

  • 触发器是⾃动执⾏的,当对触发器相关的表执⾏响应的DML操作时⽴即执⾏;
  • 触发器可以实现表中的数据的级联操作(关联操作),有利于保证数据的完整性;
  • 触发器可以对DML操作的数据进⾏更为复杂的合法性校验(如年龄大于0小于100)。

9.4.2缺点

  • 使⽤触发器实现的业务逻辑如果出现问题将难以定位,后期维护困难;
  • ⼤量使⽤触发器容易导致代码结构杂乱,增加了程序的复杂性;
  • 当触发器操作的数据量⽐较⼤时,执⾏效率会⼤⼤降低。

9.4.3存储过程和触发器使用建议

  • 对于并发量不⼤的项⽬可以选择使⽤存储过程,但是在互联⽹引⽤中不提倡使⽤存储过程(原因:存储过程时将实现业务的逻辑交给数据库处理,⼀则增减了数据库的负载,⼆则不利于数据库的迁移);
  • 在互联⽹项⽬中,应避免使用触发器。

十、视图

10.1概述

概念

视图,就是 由数据库中⼀张表或者多张表 根据特定的条件 查询出的结果构造成的 虚拟表。
作用
  • 安全性:如果我们直接将数据表授权给⽤户操作,那么⽤户可以CRUD数据表中所有数据。假如我们想要对数据表中的部分数据进⾏保护,可以将公开的数据⽣成视图,授权⽤户访问视图;⽤户通过查询视图可以获取数据表中公开的数据,从⽽达到将数据表中的部分数据对⽤户隐藏。
  • 简单性:如果我们需要查询的数据来源于多张数据表,我们可以通过视图将这些多表连接查询的结果对⽤户开放,⽤户则可以直接通过查询视图获取多表数据,操作更便捷。

视图数据的属性

当对视图进⾏操作时,对原数据表中的数据是否由影响呢?
  • 查询操作:如果在原表中添加了新的数据,⽽且这个数据满⾜创建视图时查询语句的条件,通过查询视图也可以查询出新增的数据;当删除原表中满⾜查询条件的数据时,也会从视图中删除。
  • 新增数据:如果在视图中添加数据,数据会被添加到原表。
  • 删除数据:如果在视图删除数据,数据也将从原表中删除。
  • 修改操作:如果通过修改数据,则也将修改原数据表中的数据。
视图的使⽤建议 : 复杂查询简化 操作,并且 不会对数据进⾏修改 的情况下可以使⽤视图。

10.2创建视图

语法

create view <view_name>
AS
select_statement

实例1

-- 将学⽣表中性别为男的学⽣⽣成⼀个视图
create view view1
as
select * from student where stu_gender='男';

-- 查询视图
select * from view1;

实例2

-- 创建视图:查询学⽣借书的信息(学⽣名、图书名、借书数量)
create view view2
as
select stu_name,book_name,borrow_num
from student,book,record
where student.stu_num = record.stu_num and book.book_id = record.book_id;

10.3查询视图结构、修改视图、删除视图

查询视图结构

desc view2;

修改视图

-- ⽅式1
create or replace view view1
as
select * from student where stu_gender='⼥';

-- ⽅式2
alter view view1
as
select * from student where stu_gender='男';

删除视图

  • 删除数据表时会同时删除数据表中的数据,删除视图时不会影响原数据表中的数据。(但是删除视图中的数据则会影响数据表中的数据)。
drop view view1;

十一、索引

11.1概述

  • 数据表中数据的查询速度会随着数据量的增⻓逐渐变慢 ,从⽽导致响应⽤户请求的速度变慢(⽤户体验差)。索引就是为了提⾼数据库的查询效率
  • 索引,就是将数据表中某⼀列/某⼏列的值取出来构造成便于查找的结构进⾏存储,⽣成数据表的⽬录
  • 当我们进⾏数据查询的时候,则先在⽬录中进⾏查找得到对应的数据地址,然后再到数据表中根据地址快速的获取数据记录,避免全表扫描。

索引的分类

MySQL 中的索引,根据创建索引的列的不同,可以分为:
  • 主键索引:在数据表的主键字段上创建的索引。每张表只能有⼀个主键索引。
  • 唯⼀索引:在数据表的唯⼀字段上创建的索引。每张表可以有多个唯一索引。
  • 普通索引:在数据表的普通字段上创建的索引。每张表可以有多个普通索引。
  • 组合索引:在数据表的两个及以上字段上联合创建的索引。每张表可以有多个组合索引。
注意  :
  • 在创建数据表时,将字段声明为主键(添加主键约束),会⾃动在主键字段创建主键索引。
  • 在创建数据表时,将字段声明为唯⼀键(添加唯⼀约束),会⾃动在唯⼀字段创建唯⼀索引。

11.2创建索引

11.2.1唯一索引

-- 创建唯⼀索引的字段的值不能重复
create unique index index1
on student(stu_name);

 11.2.2普通索引

-- 创建普通索引的字段的值可以重复
create index index2
on student(stu_age);

11.2.3组合索引

create index index3
on student(stu_name,stu_age);

11.2.4全文索引

MySQL 5.6 版本新增的索引,可以通过此索引进⾏全⽂检索操作,因为MySQL全⽂检索不⽀持中⽂,因此这个全⽂索引不被开发者关注,在应⽤开发中通常是通过搜索引擎(数据库中间件)实现全⽂检索。
create fulltext index <index_name> 
on 表名(字段名);

11.3索引使用

  • 索引创建完成之后⽆需调⽤,当根据创建索引的列进⾏数据查询的时候,会⾃动使⽤索引;
  • 组合索引需要根据创建索引的所有字段进⾏查询时触发。
在命令⾏窗⼝中可以查看查询语句的查询规划:
explain select * from tb_testindex where tid=250000\G;

11.4查看索引

-- 命令行
show create table student\G;

-- 查询数据表的索引
show indexes from student;

show keys from student;

11.5删除索引

-- 删除索引要指定表名(因为不同表可能存在相同的索引名)
drop index index1
on student;

11.6索引的使用总结

优点

  • 索引⼤⼤降低了数据库服务器在执⾏查询操作时扫描的数据,提⾼查询效率。
  • 索引可以避免服务器排序、将随机IO变成顺序IO。

缺点

  • 索引是根据数据表列的创建的,当数据表中数据发⽣DML操作时,索引⻚需要更新;
  • 索引⽂件也会占⽤磁盘空间。

使用建议

  • 数据表中数据不多时,全表扫⾯可能更快,不要使⽤索引;
  • 数据量⼤但是DML操作很频繁时,不建议使⽤索引;
  • 不要在数据重复读⾼的列上创建索引(性别);
  • 创建索引之后,要注意查询SQL语句的编写,避免索引失效。

十二、事务

12.1概述

12.1.1什么是事务

一个事务就是完成同一个业务的多个DML操作。

借书业务

  • 操作1:修改图书库存。
  • 操作2:添加借书记录。

转账业务(张三给李四转账1000元)

  • 操作1:张三账户-1000元。
  • 操作2:李四账户+1000元。

12.1.2事务特性(ACID)

原子性(Atomicity):一个事务中的多个DML操作,要么同时执行成功,要么同时执行失败。

一致性(Consistency):事务执行之前和执行之后,数据库中的数据是一致的。

隔离性(Isolation):数据库允许多个事务同时执行(例如张三借书的同时允许李四借书),但是多个并行的事务之间不能相互影响

持久性(Durability):事务完成之后,对数据库的操作是永久的。

12.2事务管理

在MySQL中,默认DML操作的执行是自动提交的(即当我们执行一个DML操作之后,DML操作会被写入连接缓存,然后自动提交到数据文件)。

事务管理流程

  1. 在开始事务的第一个DML操作之前,执行开启事务
  2. 依次执行事务中的每个DML操作
  3. 如果在执行过程中出现异常,则执行回滚事务
  4. 如果事务中所有的DML都执行成功,则在最后执行提交事务
-- 借书业务
-- 开始事务(关闭自动提交,即开启手动提交)
start transaction;

-- 操作1:修改图书库存
update book
set book_stock = book_stock - 2
where book_id = 1;

-- 错误操作
select aaa;
-- 事务回滚(清除连接缓存中的操作)
rollback;

-- 操作2:添加借书记录
insert into record(stu_num,book_id,borrow_num,is_return,borrow_date)
values('1001',1,2,0,now());

-- 提交事务(将连接缓存的操作写入数据文件中)
commit;

12.3事务隔离级别

12.3.1读未提交(read uncommitted)

读未提交会导致脏读。

  • 脏读:一个事务读取到另一个事务中未提交(连接缓存)的数据。

12.3.2读已提交(read committed)

一个事务只能读取另一个事务已经提交的数据。避免了脏读,但是会导致不可重复读(虚读)。

  • 不可重复读(虚读):在同一个事务中,两次查询操作读取到的数据不一致。

12.3.3可重复读(repeatable read)

一个事务在第一次查询之后,在事务结束之前其他事务不能修改对应的数据。避免了虚读,但是可能会导致幻读。

  • 幻读:一个事务先修改数据表中的数据,然后另一个事务新增一条数据,接着第一个事务查询结果,发现结果中多了一条数据(第二个事务添加的)。

12.3.4串行化(serializable)

同时只允许一个事务对数据进行操作。避免了脏读、虚读、幻读。

总结

隔离级别脏读虚读幻读
read uncommitted
read committed×
repeatable read××
serializable×××

12.3.5设置数据库事务隔离级别

控制事物间的隔离性的两种方式:

  • 设置数据库事务隔离级别控制。
  • 通过客户端与数据库连接控制。

查看数据库默认的事务隔离级别

-- 在MySQL8.0.3 之前
select @@tx_isolation;

-- 在MySQL8.0.3之后
select @@transaction_isolation;

设置数据库事务隔离级别

-- 设置数据库的事务隔离性为‘读已提交’
set session transaction isolation
level read committed;

十三、数据库设计

我们需要设计合理的数据表完成数据存储,同时方便提取应用系统所需要的数据。

13.1数据库设计流程

数据库存储的数据是有应用系统决定的。

应用系统开发的第一步就是需求分析(应用系统的功能需求)。

1.根据应用系统的功能,分析数据实体(存储的数据对象)

电商系统:商品、用户、订单

2.提取实体的数据项(实体属性)

3.根据三范式规范实体数据项

若实体数据项不满足三范式,可能会导致数据冗余,进而引起数据维护困难、数据不一致等问题

4.绘制E-R图(实体关系图,直观的展示实体与实体之间的关系)

5.数据库建模

  • 三线图进行数据表设计
  • PowerDesigner
  • PDMan

6.建库建表(编写SQL创建数据库表)

7.SQL测试(添加数据测试)

13.2数据库设计三范式

13.2.1第一范式

要求数据表中的字段(列)不可再分。

13.2.2第二范式

不存在非关键字段对关键字段的部分依赖。

 

13.2.3第三范式

不存在非关键字段之间的传递依赖。

13.3数据库建模

13.3.1E-R图

E-R (Entity-Relationship) 实体关系图,⽤于直观的体现实体与实体之间的关联关系(⼀对⼀、⼀对多、多对⼀、多对多)。

13.3.2三线图

每个实体创建一张数据表 

一对一:在任意一端创建外键与另一端的主键关联,并且将外键设置为unique。

多对一:在多端添加外键与一端的主键关联。

多对多:额外创建一个数据表(创建两个外键和一个联合主键,每个外键与每个多端的主键关联),维护两个实体的关系。

三线图举例

13.3.3PowerDesigner(PD) 

在企业项⽬开发,我们通常是不会使⽤建模⼯具来⽣成数据表、实体类的,因为⽣成的代码规范不合乎我们的代码需求。

13.3.4PDMan

  • 下载安装PDMan。
  • 创建项目(在项目中创建数据表)。
  • 在项目中生成关系图。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值