MySQL入门:https://blog.csdn.net/m0_60121089/article/details/125521332
目录
8.4.1局部变量
八、存储过程
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概述
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
- 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概述
- 数据表中数据的查询速度会随着数据量的增⻓逐渐变慢 ,从⽽导致响应⽤户请求的速度变慢(⽤户体验差)。索引就是为了提⾼数据库的查询效率。
- 索引,就是将数据表中某⼀列/某⼏列的值取出来构造成便于查找的结构进⾏存储,⽣成数据表的⽬录。
- 当我们进⾏数据查询的时候,则先在⽬录中进⾏查找得到对应的数据地址,然后再到数据表中根据地址快速的获取数据记录,避免全表扫描。
索引的分类
- 主键索引:在数据表的主键字段上创建的索引。每张表只能有⼀个主键索引。
- 唯⼀索引:在数据表的唯⼀字段上创建的索引。每张表可以有多个唯一索引。
- 普通索引:在数据表的普通字段上创建的索引。每张表可以有多个普通索引。
- 组合索引:在数据表的两个及以上字段上联合创建的索引。每张表可以有多个组合索引。
- 在创建数据表时,将字段声明为主键(添加主键约束),会⾃动在主键字段创建主键索引。
- 在创建数据表时,将字段声明为唯⼀键(添加唯⼀约束),会⾃动在唯⼀字段创建唯⼀索引。
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全文索引
create fulltext index <index_name>
on 表名(字段名);
11.3索引使用
- 索引创建完成之后⽆需调⽤,当根据创建索引的列进⾏数据查询的时候,会⾃动使⽤索引;
- 组合索引需要根据创建索引的所有字段进⾏查询时触发。
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操作会被写入连接缓存,然后自动提交到数据文件)。
事务管理流程
- 在开始事务的第一个DML操作之前,执行开启事务。
- 依次执行事务中的每个DML操作。
- 如果在执行过程中出现异常,则执行回滚事务。
- 如果事务中所有的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图
![](https://img-blog.csdnimg.cn/56c4ced7db4e4e4291d1886c9aaecfc4.png)
13.3.2三线图
每个实体创建一张数据表
一对一:在任意一端创建外键与另一端的主键关联,并且将外键设置为unique。
多对一:在多端添加外键与一端的主键关联。
多对多:额外创建一个数据表(创建两个外键和一个联合主键,每个外键与每个多端的主键关联),维护两个实体的关系。
三线图举例
![](https://img-blog.csdnimg.cn/6a651232cc8a41018d3d7f34383d4788.png)
13.3.3PowerDesigner(PD)
在企业项⽬开发,我们通常是不会使⽤建模⼯具来⽣成数据表、实体类的,因为⽣成的代码规范不合乎我们的代码需求。
13.3.4PDMan
- 下载安装PDMan。
- 创建项目(在项目中创建数据表)。
- 在项目中生成关系图。