您现在的位置是:网站首页 > 心得笔记
关系型数据库中的外键
1、主键的概念
对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。
例如,假设我们把name字段作为主键,那么通过名字小明或小红就能唯一确定一条记录。但是,这么设定,就没法存储同名的同学了,因为插入相同主键的两条记录是不被允许的。
所以,选取主键的一个基本原则是:不使用任何业务相关的字段作为主键。
因此,身份证号、手机号、邮箱地址这些看上去可以唯一的字段,均不可用作主键。
| id(主键) | name | sex | age |
| 1 | 红红 | 女 | 20 |
| 2 | 蓝蓝 | 男 | 22 |
2、联合主键的概念
关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。
对于联合主键,允许一列有重复,只要不是所有主键列都重复即可:
| id | type | sex | name |
| 1 | A | 20 | test1 |
| 2 | A | 20 | test2 |
| 2 | B | 22 | test3 |
如果我们把上述表的id和type这两列作为联合主键,那么上面的3条记录都是允许的,因为没有两列主键组合起来是相同的。
没有必要的情况下,我们尽量不使用联合主键,因为它给关系表带来了复杂度的上升。
3、外键
3.1、一对多
当我们用主键唯一标识记录时,我们就可以在students表中确定任意一个学生的记录:
| id | name |
| 1 | test1 |
| 2 | test2 |
| 3 | test3 |
我们还可以在classes表中确定任意一个班级记录:
| id | name |
| 1 | 一班 |
| 2 | 二班 |
但是我们如何确定students表的一条记录,例如,id=1的test1,属于哪个班级呢?
由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个classes的记录可以对应多个students表的记录。
为了表达这种一对多的关系,我们需要在students表中加入一列class_id,让它的值与classes表的某条记录相对应:
| id | class_id | name |
| 1 | 1 | test1 |
| 2 | 2 | test2 |
| 3 | 2 | test3 |
这样,我们就可以根据class_id这个列直接定位出一个students表的记录应该对应到classes的哪条记录。
例如:
test1的
class_id是1,因此,对应的classes表的记录是id=1的一班;test2的
class_id是2,因此,对应的classes表的记录是id=1的二班;test3的
class_id是2,因此,对应的classes表的记录是id=2的二班。
在students表中,通过class_id的字段,可以把数据与另一张表关联起来,这种列称为外键。
3.2、多对多
通过一个表的外键关联到另一个表,我们可以定义出一对多关系。有些时候,还需要定义“多对多”关系。例如,一个老师可以对应多个班级,一个班级也可以对应多个老师,因此,班级表和老师表存在多对多关系。
多对多关系实际上是通过两个一对多关系实现的,即通过一个中间表,关联两个一对多关系,就形成了多对多关系:
teachers表:
| id | name |
| 1 | 赵老师 |
| 2 | 钱老师 |
| 3 | 孙老师 |
classes表:
| id | name |
| 1 | 一班 |
| 2 | 二班 |
中间表teacher_class关联两个一对多关系:
| id | teacher_id | class_id |
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 3 | 2 |
通过中间表teacher_class可知teachers到classes的关系:
id=1的赵老师对应id=1,2的一班和二班;id=2的钱老师对应id=1,2的一班和二班;id=3的孙老师对应id=2的二班;
同理可知classes到teachers的关系:
id=1的一班对应id=1,2的赵老师、钱老师;id=2的二班对应id=1,2,3的赵老师、钱老师和孙老师;
因此,通过中间表,我们就定义了一个“多对多”关系。
3.3、一对一
一对一关系是指,一个表的记录对应到另一个表的唯一一个记录。
例如,students表的每个学生可以有自己的联系方式,如果把联系方式存入另一个表contacts,我们就可以得到一个“一对一”关系:
| id | name | phone |
| 1 | 赵赵 | 15655252352 |
| 2 | 钱钱 | 17641525523 |
既然是一对一关系,其实可以直接给students表增加一个phone列,这样就能合二为一了?
如果业务允许,完全可以把两个表合为一个表。
还有一些应用会把一个大表拆成两个一对一的表,目的是把经常读取和不经常读取的字段分开,以获得更高的性能。例如,把一个大的用户表分拆为用户基本信息表user_info和用户详细信息表user_profiles,大部分时候,只需要查询user_info表,并不需要查询user_profiles表,这样就提高了查询速度。
4、条件查询
使用SELECT * FROM <表名>可以查询到一张表的所有记录。但是,很多时候,我们并不希望获得所有记录,而是根据条件选择性地获取指定条件的记录,例如,查询分数在80分以上的学生记录。在一张表有数百万记录的情况下,获取所有记录不仅费时,还费内存和网络带宽。
条件查询的语法:
SELECT * FROM <表名> WHERE <条件表达式>
条件表达式第一种条件:可以用<条件1> AND <条件2>表达满足条件1并且满足条件2。例如,符合条件“分数在80分或以上”,并且还符合条件“男生”,把这两个条件写出来:
条件1:根据score列的数据判断:
score >= 80;条件2:根据gender列的数据判断:
gender = 'M',注意gender列存储的是字符串,需要用单引号括起来。
条件表达式第二种条件:<条件1> OR <条件2>,表示满足条件1或者满足条件2。例如,把上述AND查询的两个条件改为OR,查询结果就是“分数在80分或以上”或者“男生”,满足任意之一的条件即选出该记录
条件表达式第三种条件:NOT <条件>,表示“不符合该条件”的记录。例如,写一个“不是2班的学生”这个条件,可以先写出“是2班的学生”:class_id = 2,再加上NOT:NOT class_id = 2
上述NOT条件NOT class_id = 2其实等价于class_id <> 2,因此,NOT查询不是很常用。
优先级:如果不加括号,条件运算按照NOT、AND、OR的优先级进行,即NOT优先级最高,其次是AND,最后是OR。加上括号可以改变优先级。
下一篇:鼠标悬停按钮动画