数据库设计
1. 数据库设计是什么
所谓的数据库设计就是根据需求文档的描述将需求转成数据库的存储结构的过程.
在数据库设计的流程上,我们通常根据需求,画出数据的ER图.然后在通过ER图生成数据库的建库脚本. (Entity Relational)
ER图,所谓的ER图就是数据库关系图
为什么我们使用ER图来实现数据库设计的设计呢?
1.可见即可得.使用ER图可以通过图形的方式展示表与表直接的关系
2.可以根据设置的数据库,方便生成不同的数据库的SQL建库脚本
3.可以快速的生成数据库文档
小结:所谓的数据库设计,就是通过ER图,根据需求给数据库建表表结构!!!
2. 为什么需要数据库设计
软件开发都是分别从页面设计和数据库设计开始的.
创建项目的数据库是项目开发必须的阶段.
3. 数据库设计基础理论(重点)
3.1. 数据库设计的步骤
数据库设计的步骤是根据需求的描述:
第一步:标识表
第二步:标识表的字段
第三步:标识表与表之间的关系
3.2. 标识表注意事项
所谓的标识表,就是根据需求将表创建!!
我们在标识表的时候,可以将表分为实体表和业务表.
所谓的实体表:就是记录需求中描述为一个对象(名词)的表.如:用户,商品,订单,管理员,角色等
所谓的业务表:就是记录在需求中描述为一个业务行为(动词)的表:收藏,关注,等 (大部分是中间表)
虽然没有强制的规定先标识实体表还是业务表,但我们通常在标识表时会先标识实体表,再标识业务表.
因为业务表一般是用于标识实体表与另一个实体的多对多的关系的.
3.3. 标识字段注意事项
标识字段,在数据库设计中,尽量符合数据库设计的三大范式原则.
--三大范式,就是用于数据库设计,标识字段的时候使用的!!!。
数据库表设计三大范式最终解决的是数据冗余问题
3.4. 三大范式
为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。
在实际开发中最为常见的设计范式有三个
3.4.1. 第一范式(确保每列保持原子性)
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。
上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。
3.4.2. 第二范式(确保表中的每列都和主键相关)
非主键列必须依赖主键列存在
第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。
订单信息表
这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。
而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。
这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。
3.4.3. 第三范式(确保每列都和主键列直接相关,而不是间接相关-在2NF基础上消除传递依赖)
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。
这样在查询订单信息的时候,就可以使用客户编号来引用客户信息表中的记录,也不必在订单信息表中多次输入客户信息的内容,减小了数据冗余。
三大范式只是一般设计数据库的基本理念,可以建立冗余较小、结构合理的数据库。如果有特殊情况,当然要特殊对待,数据库设计最重要的是看需求跟性能,需求>性能>表结构(范式)。所以不能一味的去追求范式建立数据库。
3.5. 实际开发部符合三范式的场景
3.5.1. 示例-性能的要求出现的冗余数据
我们很多系统都要记录日志.而日志里面,必须要包括用户的信息.如果严格按照三大方式.日志的用户信息必须是从用户表中获得,日志每天都会出现巨量的数据。如果关联用户表查询,整理日志时会导致用户表的访问大大被拖慢。
所以,我们会将用户的信息直接写在日志表里面。在日志表中写用户的的信息,明显违反了第二范式,基于查询的性能的需要,一般日志表的用户信息是冗余。
3.5.2. 示例-业务逻辑的要求出现的冗余数据
我们在订单中有一个商品的价格、商品名字。而这个商品的价格直接就是订单的字段.并不是商品表里面商品的价格.明显违反了第三范式.
但业务上,由于订单的商品的价格不能随着着商家修改了商品价格而修改.所以像这种需求下,必须要给订单表一个冗余的商品价格字段。
3.6. 标识表与表之间的关系
表与表之间的关系包括有
3.6.1. 一对一
3.6.2. 一对多/多对一
3.6.3. 多对多
3.6.3.1. 遵循范式设计
3.6.3.2. 以实际需求为主的设计
数据库表与表的关系,就是也需求描述的从属关系。
问题:表与表之间的关系,是谁决定的?
答:需求决定的!!!
4. 小结(重点)
- 数据库设计就是建立项目的表结构
- 基于数据的复杂性,一般数据库数据库是先画ER图的。
- 数据库设计的步骤是:标识表,标识字段,标识表与表之间关系
标识表,先标识实体表,在标识业务表
实体表(名词,没有行为)
业务表(包括业务动作,一般就是一个中间表)
标识字段,必须要求理解三大范式
为什么需要三大范式,避免数据的冗余,导致数据的异常。
数据库设计总体上要符合三大范式,但是基于业务需求和性能要求,有时候可以有少许的冗余。数据设计冗余,设计者必须要说明原因(项目需求需要)
标识表与表之间的关系
- 有哪些表与表之间关系,一对多,多对一,一对一,多对多
- 表与表之间的关系是由需求决定,讨论表之间的关系前,必须要先确定需求
- 关系数据库是不能直接支持多对多的业务关系的,如果出现多对多必须要拆分一个中间表,原因是数据库里面的字段不能存储一个集合数据。
5. ERMaster的使用
ERMaster 是集成到Eclipse开发工具的一个插件。
5.1. ERMaster安装
-
1.复制到dropins文件夹,解压到当前文件
- 2.打开eclipse。在新建的other里面有ermaster选项,表示成功
5.2. DDL导出
在工作区,右击
5.3. 导出文档
右击工作区,导出HTML
6. 综合案例-学生成绩管理系统
需求:设计一个学生成绩管理系统
- 首先有学生,学生必须包括登录、禁用功能
- 基于安全性的考虑,学生的身份信息要单独存储。
- 一个学生有多门成绩
- 一个学生可以有多个老师,一个老师也可以教多个学生
第一步:标识表,标识实体表。学生、学生身份、成绩、老师
第二步:标识字段
第三步:标识表与表之间的关系
6.1. ER设计图
6.2. SQL语句
导出sql语句把逻辑名称作为注释
sql
SET SESSION FOREIGN_KEY_CHECKS=0;
/* Drop Tables */
DROP TABLE IF EXISTS student_teacher; DROP TABLE IF EXISTS tb_info; DROP TABLE IF EXISTS tb_score; DROP TABLE IF EXISTS tb_student; DROP TABLE IF EXISTS tb_teacher;
/* Create Tables */
-- student_teacher CREATE TABLE student_teacher ( stu_id int NOT NULL COMMENT '学生id', teacher_id int NOT NULL COMMENT '主键' ) COMMENT = 'student_teacher';
-- 学生身份信息 CREATE TABLE tb_info ( stu_idcard varchar(50) COMMENT '身份证', stu_address varchar(100) COMMENT '地址', stu_phone varchar(50) COMMENT '电话', stu_email varchar(50) COMMENT '邮箱', stu_id int NOT NULL COMMENT '学生id' ) COMMENT = '学生身份信息';
-- 学生成绩表 CREATE TABLE tb_score ( sc_id int NOT NULL AUTO_INCREMENT COMMENT '主键', sc_suject varchar(50) COMMENT '科目', sc_score float COMMENT '成绩', stu_id int NOT NULL COMMENT '学生id', PRIMARY KEY (sc_id) ) COMMENT = '学生成绩表';
-- 学生信息报 CREATE TABLE tb_student ( stu_id int NOT NULL AUTO_INCREMENT COMMENT '学生id', stu_name varchar(50) COMMENT '学生姓名', stu_account varchar(50) COMMENT '学生账号', stu_pwd varchar(50) COMMENT '学生密码', stu_salt varchar(100) COMMENT '密码加密字段(盐)', stu_status int DEFAULT 1 COMMENT '学生状态 1 正常 0 锁定', PRIMARY KEY (stu_id) ) COMMENT = '学生信息报';
-- 老师表 CREATE TABLE tb_teacher ( teacher_id int NOT NULL AUTO_INCREMENT COMMENT '主键', teacher_name varchar(50) COMMENT '姓名', teacher_account varchar(50) COMMENT '账号', teacher_pwd varchar(50) COMMENT '密码', teacher_phone varchar(50) COMMENT '老师电话', PRIMARY KEY (teacher_id) ) COMMENT = '老师表';
/* Create Foreign Keys */
ALTER TABLE student_teacher ADD FOREIGN KEY (stu_id) REFERENCES tb_student (stu_id) ON UPDATE RESTRICT ON DELETE RESTRICT ;
ALTER TABLE tb_info ADD FOREIGN KEY (stu_id) REFERENCES tb_student (stu_id) ON UPDATE RESTRICT ON DELETE RESTRICT ;
ALTER TABLE tb_score ADD FOREIGN KEY (stu_id) REFERENCES tb_student (stu_id) ON UPDATE RESTRICT ON DELETE RESTRICT ;
ALTER TABLE student_teacher ADD FOREIGN KEY (teacher_id) REFERENCES tb_teacher (teacher_id) ON UPDATE RESTRICT ON DELETE RESTRICT ;
|
7. 综合案例-CRM系统
Customer Relational Manager System (客户关系管理系统)
7.1. 客户调研
客户人员是一个不懂软件设计的人,它希望有一个可以做管理系统来管理他的员工.
描述如下:
系统的需求调研 1.每个销售人员只能查看自己的客户. (销售不能互相看客户) 2.客户是公司,有多个联系人 (客户主体公司) 3.销售可以指定客户给另一个销售人员跟进 (销售人员可以转移客户给另外销售) 4.销售人员离职时,可以禁用不让登录 5.管理员可以修改销售人员的密码 (后台管理人员可以管理所有信息) 6.销售人员可以设置重点跟进客户,并且可以说明重点跟进客户的原因. 7.销售人员可以多次跟进同一个客户(骚扰) |
7.2. 系统功能列表(需求文档的核心部分)
功能列表给到客户人员。给到开发人员,跟进需求文档设计数据库。
前端功能(销售人员的功能) |
1.客户管理 |
2.联系人管理 |
|
3.转移客户 |
|
4.跟进客户 |
|
5.标记重点客户 |
|
后台管理(管理员管理 RBAC) |
1.客户管理 (全局) |
2.联系人管理(全局) |
|
3.管理员管理 |
|
4.角色管理 |
|
5.权限管理 |
7.3. 数据库设计
根据功能列表与客户调研报告(需求文档),设计数据数据库。
第一步:RBAC系统的设计
Role Based Access Control (基于角色的权限控制系统);就是根据不同的用户,根据用于所属的角色不同而登录的界面就不同。
需求:一个管理员只有一个角色(单角色的设计),一个角色可以有多个管理员
一个就是可以有多个权限,一个权限也可以有在多个角色角色。
第二步:业务系统的设计(CRM)
需求:
- 一个销售(用户),只能查看自己的客户
- 客户是公司,有多个联系人 (客户主体公司)
- 销售人员可以设置重点跟进客户,并且可以说明重点跟进客户的原因.
- 销售人员离职时,可以禁用不让登录
- 销售人员可以多次跟进同一个客户(骚扰)
- 销售可以指定客户给另一个销售人员跟进
8. PowerDesigner数据设计工具
PowerDesigner是一个专业的数据库设计软件。现在市场主流的数据库设计工具!!
8.1. 使用步骤
8.1.1. 第一步:创建一个物理ER图
--不要创建逻辑ER图,逻辑ER图只能看,不能生成数据脚本。
8.1.2. 第二步:工具栏说明
- 表示创建包
- 表示创建表
- 表示创建表与表之间的关系
8.1.3. 第三步:取消Name To Code
8.1.4. 第四步:CRM系统的设计
问题:设计表的时候,发现没有ID自增长选项
答:是因为默认没有指定Mysql数据库。所以需要设计为MySQL数据库。
--修改后,发现多个一个Identity选项
设计图如下:
8.1.5. 第四步:导出SQL脚本
--设置导出的路径
--生成的SQL脚本
/*==============================================================*/ /* DBMS name: MySQL 5.0 */ /* Created on: 2019/6/16 23:29:00 */ /*==============================================================*/
drop table if exists tb_user;
/*==============================================================*/ /* Table: tb_user */ /*==============================================================*/ create table tb_user ( user_id int not null auto_increment comment '用户id', user_name varchar(50) comment '用户姓名', user_account varchar(50) comment '用户账号', user_password varchar(50) comment '密码', user_status int comment '1 可用,0 锁定', primary key (user_id) );
alter table tb_user comment '这是用户表,存储用户信息';
|
问题:如何显示注释字段
9. RBAC系统数据库设计
RBAC (Role Based Access Control) 基于角色的权限系统。
RBAC系统可以实现根据不同的用户,显示的权限(菜单-功能)就不一样。
- 用户
- 角色
- 权限
需求:
- 一个用户只要一个角色 (单角色的设计)
- 一个角色可以有多个用户
- 一个角色可以有多个权限
- 一个权限可以属于多个角色
用户与角色的关系:N :1
角色与权限的关系:N :N
9.1. ER图
9.2. SQL语句
/*==============================================================*/ /* DBMS name: MySQL 5.0 */ /* Created on: 2019/6/17 15:27:54 */ /*==============================================================*/
drop table if exists tb_permission;
drop table if exists tb_role;
drop table if exists tb_role_permission;
drop table if exists tb_user;
/*==============================================================*/ /* Table: tb_permission */ /*==============================================================*/ create table tb_permission ( permission_id int not null auto_increment comment '权限id', permission_name varchar(50) comment '权限名称', permission_url varchar(150) comment '权限url地址', perimssion_expression varchar(100) comment '权限表达式', permission_type int comment '是否是菜单权限 0 普通权限 1 菜单权限', permission_parent_id int comment '权限父id', permission_sorting int comment '权限菜单时候排序升序', primary key (permission_id) );
/*==============================================================*/ /* Table: tb_role */ /*==============================================================*/ create table tb_role ( role_id int not null auto_increment comment '角色id', role_name varhcar(50) comment '角色名称', user_desc varchar(500) comment '角色描述', primary key (role_id) );
/*==============================================================*/ /* Table: tb_role_permission */ /*==============================================================*/ create table tb_role_permission ( role_id int comment '角色id', permission_id int comment '权限id', role_permission_id int not null auto_increment comment '主键', primary key (role_permission_id) );
/*==============================================================*/ /* Table: tb_user */ /*==============================================================*/ create table tb_user ( user_id int not null auto_increment comment '用户id', user_name varchar(50) comment '名称', user_account varchar(50) comment '账号', user_pwd varchar(50) comment '密码', user_salt varchar(50) comment '盐(密码加密)', user_desc varchar(500) comment '描述', user_status int comment '状态 1 可用,0锁定 ,2 ,删除', role_id int comment '角色id', primary key (user_id) );
alter table tb_role_permission add constraint FK_Reference_2 foreign key (role_id) references tb_role (role_id) on delete restrict on update restrict;
alter table tb_role_permission add constraint FK_Reference_3 foreign key (permission_id) references tb_permission (permission_id) on delete restrict on update restrict;
alter table tb_user add constraint FK_Reference_1 foreign key (role_id) references tb_role (role_id) on delete restrict on update restrict;
|