注: 参考自 http://blog.csdn.net/qq_35246620/article/details/70823903
索引:系统根据某种算法,将已有的数据(未来可能新增的数据),单独建立一个文件,这个文件能够实现快速匹配数据,并且能够快速的找到对应的记录,几乎所有的索引都是建立在字段之上的。
索引的意义:
但是增加索引是有前提条件的,这是因为索引本身会产生索引文件(有的时候可能会比数据本身都大),因此非常耗费磁盘空间。
MySQL 中提供了多种索引,包括:
primary key
unique key
fulltext index
index
其中,主键和唯一键咱们之前已经了解过啦!至于普通索引,顾名思义,并没有什么特色,唯一的任务就是加快数据的查询速度。
在这里,咱们说说全文索引。全文索引,即根据文章内部的关键字进行索引,其最大的难度就是在于如何确定关键字。对于英文来说,全文索引的建立相对容易,因为英文的两个单词之间有空格;但是对于中文来说,全文索引的建立就比较难啦,因为中文两个字之间不仅没有空格,而是还可以随意组合。
在数据库中,将实体与实体的关系反应到表的设计上来,可以细分为 3 种,分别为:一对一(1:1)
,一对多(1:N)
(或多对一(N:1)
)和多对多(N:N)
。
在此,所有的关系都是指表与表之间的关系。
一对一,即一张表的一条记录只能与另外一张表的一条记录相对应,反之亦然。
例如,咱们设计一张「个人信息表」,其字段包含:姓名、性别、年龄、身高、体重、籍贯和居住地等。
ID | 姓名 | 性别 | 年龄 | 身高 | 体重 | 籍贯 | 居住地 |
---|---|---|---|---|---|---|---|
1 | Charies | 男 | 18 | 182 | 75 | 中国 | 北京 |
2 | Swift | 女 | 18 | 172 | 50 | 美国 | 纽约 |
如上表所示,基本满足咱们的要求,其中姓名、性别和年龄属于常用数据,但是身高、体重、籍贯和居住地为不常用数据。如果每次查询都要查询所有数据的话,那么不常用数据就会影响效率,而且又不常用。因此,咱们可以将常用的数据和不常用的数据分离存储,即分为两张表,例如:
表 1:常用数据
ID | 姓名 | 性别 | 年龄 |
---|---|---|---|
1 | Charies | 男 | 18 |
2 | Swift | 女 | 18 |
表 2:不常用数据
ID | 身高 | 体重 | 籍贯 | 居住地 |
---|---|---|---|---|
1 | 182 | 75 | 中国 | 北京 |
2 | 172 | 50 | 美国 | 纽约 |
如上面表1
和表2
所示,通过字段ID
,表1
中的一条记录只能匹配表2
中的一条记录,反之亦然,这就是一对一
的关系。
一对多,即一张表中的记录可以对应另外一张表中的多条记录,但是反过来,另外一张表中的一条记录只能对应第一张表中的一条记录。
例如,咱们设计「国家城市表」,其包含两个实体,即国家和城市。
表 3:国家表
COUNTRY_ID | 国家 | 位置 |
---|---|---|
1 | 中国 | 亚洲 |
2 | 美国 | 北美洲 |
3 | 俄罗斯 | 亚洲和欧洲 |
表 4:城市表
CITY_ID | 城市 | 国家 |
---|---|---|
1 | 北京 | 中国 |
2 | 深圳 | 中国 |
3 | 纽约 | 美国 |
4 | 莫斯科 | 俄罗斯 |
如上面表3
和表4
所示,通过字段国家
,表3
中的一条记录可以匹配表4
中的多条记录,但反过来,表4
中的一条记录只能匹配表3
中的一条记录,这就是典型的一对多
的关系。
多对多,即一张表中的记录可以对应另外一张表中的多条记录,反过来,另外一张表中的一条记录也可以对应第一张表中的多条记录。
例如,咱们设计「教师学生表」,其包含两个实体,即教师和学生。
表 5:教师表
TEA_ID | 姓名 | 性别 |
---|---|---|
1 | 刘涛 | 女 |
2 | 刘亦菲 | 女 |
3 | 刘德华 | 男 |
表 6:学生表
STU_ID | 姓名 | 性别 |
---|---|---|
1 | 齐岳 | 男 |
2 | 杜康 | 男 |
观察上面的表5
和表6
,咱们会发现:表5
和表6
的设计满足了实体的属性,但没有维护实体之间的关系,即一个老师教过多个学生,一个学生也被多个老师教过。但是无论咱们在表5
中还是在表6
中增加字段,都会出现一个问题,那就是:该字段要保存多个数据,并且还是与其他表有关系的字段,不符合设计规范。因此,咱们可以再设计一张「中间表」,专门用来维护表5
和表6
的关系。
表 7:中间表
ID | TEA_ID | STU_ID |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 1 |
4 | 3 | 2 |
观察上面的表5
、表6
和表7
,咱们会发现增加表7
之后,咱们维护表5
和表6
的关系更加方便啦!无论是想从表5
通过表7
查到表6
,还是想从表6
通过表7
查到表5
,都非常容易啦!这就是典型的多对多
的关系。
范式:Normal Farmat
,是为了解决数据的存储和优化问题。
在数据存储之后,凡是能够通过关系寻找出来的数据,坚决不再重复存储,范式的终极目标是减少数据冗余。
范式是一种分层结构的规范,共 6 层,分别为1NF
、2NF
、3NF
、4NF
、5NF
和6NF
,每一次都比上一层严格,若要满足下一层范式,其前提是先满足上一层范式。其中,1NF
是最底层的范式,6NF
为最高层的范式,也最严格。
MySQL 数据库属于关系型数据库,其存储数据的时候有些浪费空间,但也致力于节省空间,这就与范式想要解决的问题不谋而合,因此在设计数据库的时候,大都会利用范式来指导设计。但是数据库不单是要解决存储空间的问题,还要保证效率的问题,而范式只为解决存储空间的问题,所以数据库的设计又不能完全按照范式的要求来实现,因此在一般情况下,只需要满足前三种范式即可。
此外,咱们需要知道:范式在数据库的设计中是有指导意义的,但不是强制规范。
第一范式:在设计表存储数据的时候,如果表中设计的字段存储的数据,在取出来使用之前还需要额外的处理(拆分),那么表的设计就不满足第一范式,第一范式要求字段的数据具有原子性,不可再分。
例如,咱们设计一个「学校假期时间表」,如下所示:
表 1:学校假期时间表
ID(P) | 学校名称 | 起始日期,结束日期 |
---|---|---|
1 | 哈尔滨工业大学 | 20170625,20170903 |
2 | 浙江大学 | 20170630,20170901 |
观察上表,咱们会发现表1
的设计并没有什么问题,但是如果需求是查询各学校开始放假的日期呢?那显然上表的设计并不满足1NF
,数据不具有原子性。对于此类问题,解决的方案就是将表1
进行拆分:
表 2:拆分后的表 1
ID(P) | 学校名称 | 起始日期 | 结束日期 |
---|---|---|---|
1 | 哈尔滨工业大学 | 20170625 | 20170903 |
2 | 浙江大学 | 20170630 | 20170901 |
第二范式:在数据表的设计过程中,如果有复合主键(多字段主键),且表中有字段并不是由整个主键来确定,而是依赖复合主键中的某个字段(主键的部分),也就是说存在字段依赖主键的部分的问题(称之为部分依赖),第二范式就是要解决表设计中不允许出现部分依赖。
例如,咱们设计一个「教室授课表」,如下所示:
表 3:教室授课表
教师(P) | 性别 | 课程 | 授课地点(P) |
---|---|---|---|
许仙 | 男 | 《如何追到心爱的女孩》 | 杭州西湖 |
白娘子 | 女 | 《论女人的恋爱修养》 | 雷峰塔 |
白娘子 | 女 | 《如何打赢与和尚之间的持久战》 | 金山寺 |
观察上表,咱们会发现:教师不能作为独立的主键,需要与授课地点相结合才能作为主键(复合主键,每个教师的某个课程只能在固定的地点上),其中性别依赖于具体的教师,而课程依赖于授课地点,这就出现了表的字段依赖于部分主键的问题,从而导致不满足第二范式。
在此,咱们采用 方案 2 的解决方法,即取消复合主键,使用逻辑主键。
ID(P) | 教师 | 性别 | 课程 | 授课地点 |
---|---|---|---|---|
1 | 许仙 | 男 | 《如何追到心爱的女孩》 | 杭州西湖 |
2 | 白娘子 | 女 | 《论女人的恋爱修养》 | 雷峰塔 |
3 | 白娘子 | 女 | 《如何打赢与和尚之间的持久战》 | 金山寺 |
第三范式:需要满足第一范式和第二范式,理论上讲,每张表中的所有字段都应该直接依赖主键(逻辑主键,代表是业务主键),如果表设计中存在一个字段,并不直接依赖主键,而是通过某个非主键字段依赖,最终实现主键依赖(把这种不是直接依赖主键,而是依赖非主键字段的依赖关系,称之为传递依赖),第三范式就是要解决表设计中出现传递依赖的问题。
以上述的添加逻辑主键后的 表3 为例:
ID(P) | 教师 | 性别 | 课程 | 授课地点 |
---|---|---|---|---|
1 | 许仙 | 男 | 《如何追到心爱的女孩》 | 杭州西湖 |
2 | 白娘子 | 女 | 《论女人的恋爱修养》 | 雷峰塔 |
3 | 白娘子 | 女 | 《如何打赢与和尚之间的持久战》 | 金山寺 |
在以上表的设计中,性别依赖教师,教师依赖主键;课程依赖授课地点,授课地点依赖主键,因此性别和课程都存在传递依赖的问题。
表 4:教师表
TEACHER_ID(P) | 教师 | 性别 |
---|---|---|
1 | 许仙 | 男 |
2 | 白娘子 | 女 |
3 | 白娘子 | 女 |
表 5:授课地点表
ADDRESS_ID(P) | 课程 | 授课地点 |
---|---|---|
1 | 《如何追到心爱的女孩》 | 杭州西湖 |
2 | 《论女人的恋爱修养》 | 雷峰塔 |
3 | 《如何打赢与和尚之间的持久战》 | 金山寺 |
表 6:进行处理后的表
ID(P) | TEACHER_ID | ADDRESS_ID |
---|---|---|
1 | 1 | 1 |
2 | 2 | 2 |
3 | 3 | 3 |
在观察上述 表 4 和 表 5,咱们会发现TEACHER_ID
等价于教师
且ADDRESS_ID
等价于授课地点
,因此其逻辑主键并没有什么实际的限制意义,咱们只需要看其具体代表的业务主键即可。咱们之所以使用逻辑主键,是因为:逻辑主键可以实现自动增长,并且数字传递比较方便,而且有利于节省空间。
在某些特定的环境中(例如淘宝数据库),在设计表的时候,如果一张表中有几个字段是需要从另外的表中去获取数据,理论上讲,的确可以获得想要的数据,但是相对来说,其效率低会一点。此时为了提高查询效率,咱们会刻意的在某些表中,不去保存另外一张表的主键(逻辑主键),而是直接保存想要存储的数据信息,这样的话,在查询数据的时候,这张表就可以直接提供咱们想要的数据,而不需要多表查询,但是这样做会导致数据冗余。
实际上,逆规范化是磁盘利用率和效率之间的对抗。