数据库的设计
1. 多表之间的关系(三种)
1) 分类和实现关系:(红色部分重要)
关系 | 举例 | 建表原则 | |
一对一 | 人和身份证 员工表 人和简历 护照 | 人和身份证分析:一个人只有一个身份证,一个身份证只能对应一个人 | 一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。 |
一对多 (多对一) | 部门和员工 班级和学生 客户和订单 分类和商品 | 部门和员工分析:一个部门有多个员工,一个员工只能对应一个部门 | 在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键 * 用的最多 |
多对多 | 老师和学生 | 学生和课程分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择 | 需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键 |
2) 关系表图示:
综合案例:
旅游线路分类表和线路表的关系,线路和用户之间的关系图解:
2. 一对多案例:
需求:一个旅游线路分类中有多个旅游线路
图片:-- 建表 -- 创建旅游线路分类表 tab_category create table tab_category ( cid int primary key auto_increment, -- cid 旅游线路分类主键,自动增长 cname varchar(100) not null unique -- cname 旅游线路分类名称非空,唯一,字符串 100 ) -- 添加旅游线路分类数据: insert into tab_category (cname) values ('周边游'), ('出境游'), ('国内游'), ('港澳游'); select * from tab_category; -- 创建旅游线路表 tab_route create table tab_route( rid int primary key auto_increment, -- 旅游线路主键,自动增长 rname varchar(100) not null unique, -- 旅游线路名称非空,唯一,字符串 100 price double, -- 价格 rdate date, -- 上架时间,日期类型 cid int, -- 外键,所属分类 foreign key (cid) references tab_category(cid) -- 省略了constraint 外键名称 ) -- 添加旅游线路数据 INSERT INTO tab_route VALUES (NULL, '【厦门+鼓浪屿+南普陀寺+曾厝垵 高铁 3 天 惠贵团】尝味友鸭面线 住 1 晚鼓浪屿', 1499, '2018-01-27', 1), (NULL, '【浪漫桂林 阳朔西街高铁 3 天纯玩 高级团】城徽象鼻山 兴坪漓江 西山公园', 699, '2018-02- 22', 3), (NULL, '【爆款¥1699 秒杀】泰国 曼谷 芭堤雅 金沙岛 杜拉拉水上市场 双飞六天【含送签费 泰风情 广州 往返 特价团】 ', 1699, '2018-01-27', 2), (NULL, '【经典•狮航 ¥2399 秒杀】巴厘岛双飞五天 抵玩【广州往返 特价团】 ', 2399, '2017-12-23', 2), (NULL, '香港迪士尼乐园自由行 2 天【永东跨境巴士广东至迪士尼去程交通+迪士尼一日门票+香港如心海景酒店 暨会议中心标准房 1 晚住宿】 ', 799, '2018-04-10', 4); select * from tab_route;
表与表的关系图:
3. 多对多关系案例
需求:一个用户收藏多个线路,一个线路被多个用户收藏
图片:
-- 创建用户表 tab_user create table tab_user ( uid int primary key auto_increment, -- uid用户主键,自增长 username varchar(100) unique not null, -- username 用户名长度 100,唯一,非空 password varchar(30) not null, -- password 密码长度 30,非空 name varchar(100), -- name 真实姓名长度 100 birthday date, -- birthday 生日 sex char(1) default '男', -- sex 性别,定长字符串 1 telephone varchar(11), -- telephone 手机号,字符串 11 email varchar(100) -- email 邮箱,字符串长度 100 ) -- 添加用户数据 INSERT INTO tab_user VALUES (NULL, 'cz110', 123456, '老王', '1977-07-07', '男', '13888888888', '66666@qq.com'), (NULL, 'cz119', 654321, '小王', '1999-09-09', '男', '13999999999', '99999@qq.com'); select * from tab_user; -- 用户和旅游线路是多对多的关系, 需要第三方表 -- 创建收藏表 tab_favorite create table tab_favorite ( rid int, -- rid 旅游线路 id,外键 date datetime, -- date 收藏时间 uid int, -- uid 用户 id,外键 primary key(rid,uid), -- rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次 foreign key (rid) references tab_route(rid), -- 用户外键关联 foreign key(uid) references tab_user(uid) -- 路线外键关联 ) -- 增加收藏表数据 INSERT INTO tab_favorite VALUES (1, '2018-01-01', 1), -- 老王选择厦门 (2, '2018-02-11', 1), -- 老王选择桂林 (3, '2018-03-21', 1), -- 老王选择泰国 (2, '2018-04-21', 2), -- 小王选择桂林 (3, '2018-05-08', 2), -- 小王选择泰国 (5, '2018-06-02', 2); -- 小王选择迪士尼 select * from tab_favorite;
第三张表关联了用户表的id和线路表的id
第三张表中的复合主键,也称作联合主键,primary key(rid,uid) 这两个外键(rid和uid)联合起来作为tab_favorite的主键
rid和uid是不能重复的, 因为一个线路不能收藏两次
到此为止表就设计完了,看效果
3. 数据库范式
1) 范式:好的数据库设计对数据的存储性能和后期的程序开发, 都会产生重要的影响。
建立科学的, 规范的数据库就需要满足一些规则来优化数据的设计和存储, 这些规则就称为范式
2)目前关系数据库有六种范式:
第一范式(1NF) | 数据库表的每一列都是不可分割的原子数据项, 每一列不可再拆分 |
第二范式(2NF) | 在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖) |
第三范式(3NF) | 在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖) |
巴斯-科德范式(BCNF) | |
第四范式(4NF) | |
第五范式(5NF,又称完美范式) |
满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF) ,
其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就没有什么太大的问题。
2) 只介绍前三种范式
根据案例介绍
第一范式(1NF):每一列都是不可分割的原子数据项
分析 : 以上表中系就不是原子项,还可以进行分割
解决: 如下表符合第一范式, 一般创建表出来后都会满足第一范式
以上表中出现的新问题如下
第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)
几个概念:
1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
例如:学号-->姓名。 (学号,课程名称) --> 分数
分析: 姓名依赖学号, 分数依赖学号和课程名称2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值。
例如:(学号,课程名称) --> 分数
分析: 分数依赖于 学号和课程名称这个属性组, 少一个学号或者课程名称都不可以, 这是完全依赖3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值的确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称) -- > 姓名
分析: 学号就可以确定姓名, 课程名称可以忽略,这是部分函数依赖4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
例如:学号-->系名,系名-->系主任
分析: 根据学号可以确定在哪个系, 根据系可以确定哪个系主任, 那么这个学号拥有的系主任也就确定了5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
例如:该表中码为:(学号,课程名称)
* 主属性:码属性组中的所有属性 :学号 和 课程名称
* 非主属性:除过码属性组的属性
学号和课程名称可以确定分数, 分数完全依赖于学号和课程名称这个属性组, 那么这个属性组就叫做该表的码
分析: 根据第二范式 这张表中 码是学号和课程名称 我们需要消除非主属性 对 主码 的部分依赖
姓名 系名 系主任 仅 依赖于 学号 不依赖于课程名称,这是部分依赖,我们就是要消除部分依赖
第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
学生表中 学号 被 系名 所依赖, 系主任 依赖于 系名 , 系主任传递依赖于学号, 消除这种传递依赖 ,
需要把学生表再分成两张表来解决传递依赖的问题
存在的问题得以全部解决了!
学了以上范式在后续做数据库设计 业务分析的时候,应用这些范式设计出更加合理的数据库表
<结束 >
重点是表关系:
表与表的关系 | 关系的维护 |
一对多 | 主外键的关系 |
多对多 | 中间表,两个一对多 |
一对一 | 1) 特殊一对多,从表中的外键设置为唯一 2) 从表中的主键又是外键 |