您现在的位置是:网站首页 > 心得笔记
关系型数据库中的外键
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
。加上括号可以改变优先级。
下一篇:鼠标悬停按钮动画