浅谈mysql的外键作用
今天主要是要理顺一下mysql中的外键作用。
首先理清几个概念:
1、在mysql中,只有innodb引擎才有外键功能,myisam没有外键功能。
2、本表中的外键是另外一个表的主键
3、外键、主键都是表的索引(key,或者index),这点很重要,一对一关系就是用外键的唯一索引来做的。
4、如果A表有一个字段ID(主键),B表有一个映射到A表的外键A_ID字段。那么,我们称B表是从表(子表),A表是主表(父表)。区分这两种表有什么用呢?我们等下所说的一对多关系,一是主表(父表),多是从表(子表),别搞混淆。
下面我们来看个图。
上图student表有一个映射到class ID的外键,因而class跟student是一对多的关系,一个班级可以有N个学生,但一个学生只能有一个班级,从表是student,主表是class。
好了,在说外键作用之前,我们先搞清楚3个很重要的概念:一对一,一对多,多对多。
首先是一对一:
概念:A表中的每一个元素只能对应到B表的一个元素,B表的每一个元素只能对应到A表的一个元素。生活中有很多例子,我就不举例了。
刚才说了,从表是一对多中的多,那么通过外键能不能建立一对一的关系呢?一开始说了,外键也是个索引,只要我们把外键这个索引设置为“唯一索引(unique)”就可以实现一对一关系了。
CREATE TABLE `student` (
`ID` int(10) NOT NULL AUTO_INCREMENT,
`NAME` varchar(30) DEFAULT NULL,
`SEX` tinyint(5) DEFAULT NULL,
`CLASS_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `FK_student` (`CLASS_ID`), //本来是 KEY `FK_student` (`CLASS_ID`),
CONSTRAINT `FK_student` FOREIGN KEY (`CLASS_ID`) REFERENCES `class` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
`ID` int(10) NOT NULL AUTO_INCREMENT,
`NAME` varchar(30) DEFAULT NULL,
`SEX` tinyint(5) DEFAULT NULL,
`CLASS_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `FK_student` (`CLASS_ID`), //本来是 KEY `FK_student` (`CLASS_ID`),
CONSTRAINT `FK_student` FOREIGN KEY (`CLASS_ID`) REFERENCES `class` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
现在就是一对一关系了,一个班只有一个学生,一个学生只上一个班。
注意了:哪怕是一对一关系,但两个表不是平等地位的!一样有主表从表,有父表子表。
一对多(习惯叫一对多,不叫多对一):
一对多关系我觉得是最常用的。
概念:A表中的每一个元素在B表中可以找到多个元素与之对应,但是,在B表中的每一个元素只能在A表中找到唯一一个元素与之对应。
好像,每一个班级都能找到N个学生与之对应,但是,每个学生却只能找到一个班与之对应。
刚才也说了,外键不是唯一索引都可以构成一对多关系,主表是一,从表是多。
多对多
这个关系稍复杂了点,但也是很常用的。
概念:A表中的每一个元素在B表中可以找到多个元素与之对应,同样,在A表中的每一个元素也能在B表中找到N元素与之对应。
举个例子:一群朋友去吃饭,饭桌上N个菜,每个菜都可以让你们所有人吃。同样,你们每个人也可以吃所有的菜。(哈,这例子似乎不咋地)
通过外键,我们可以容易建立一对多关系,把外键设成唯一索引可以建立一对一关系。那么多对多关系怎样建立呢?这里要复杂一点,要建立三个表,其中有一个是中间表。
我们改一改上面的情境,class是兴趣班,一个学生可以参加多个兴趣班,一个兴趣班也有多个学生。
看到没有,我们建立了一个中间表student_class,里面放什么东西?没错,放映射到student ID和映射到class ID的外键,不干别的,为了促成多对多关系,它放外键就可以了。
现在就是两个主表,一个公共从表了。
=================================================================================================
好了,现在得说外键的作用了。
有没有发现,就算没有建立外键,我就建立几个表,一对一、一对多建立两个表,多对多建立三个表,然后字段不变,然后把一些XXX_ID字段设为索引,表之间的逻辑连线只在我们的脑海里,实际上表间是没什么联系的。这样,是不是也能完成我们的CRUD操作?
答案是可以的!为什么不可以啊?如果没有外键就不能建立表间的逻辑关系,你叫myisam这引擎怎么混啊?毕竟,这些表间的联系都是咱们数据库设计者脑海中的逻辑。
举个例子:
这两个表除了没有外键,跟上面的一样。我们来实现以下CRUD。
R(我们要查询student表,要把class_id转换成class_name):so easy是吧?你用嵌套查询也行,join也行,一下子就能实现关联查询了。
C(我们要创建一个新的班,然后又建立一个新的学生,让这个学生加入这个班):也不难,我们create一个新的班,然后得到一个insert id,然后就create
学生,把刚才的insert id填充到class_id。
U(我们要修改一下class表的NAME,student表也能更新):so easy,我们查询student表的时候时候是用join的,所以只要student中的class_id还在,我们都
能把class表对应的数据查出来。
D(我们要把一个班删掉,而且要把stundet中选了这个班的学生的CLASS_ID设置为null):这个似乎又点难度,不过我们分两步来:我们首先是得到一个class表的id值,我们先把class表中的id对应的班级删掉,然后在student中update,where条件是CLASS_ID = id。OK,分两步可是可以完成任务。
=================================================================================================
CRUD似乎都能不靠外键也能完成。那么外键有什么用呢?我只能说,上面种种的操作其实是很啰嗦的,使用外键可以很便捷的完成。
我们来重新审视一下上面的CRUD。
R:怎么弄也就是关联查询的了,这有没外键应该关系不大,不过有了外键可以比较明确我要join那个,也就是明确点而已,没什么实际上的机制帮助。
C:我们应该多考虑一种情况,我们在创建或者修改student表中的CLASS_ID的时候,会不会出现那个CLASS_ID实际上是不存在?也就是说,在class表中是没有这个ID。这情况会不会存在?哈,那是肯定会存在的。而且很容易发生。如果像刚才,这个情况完全没得检测,只好等查询的时候,哦,查不出班级名称了,再去检查,尼玛,原来CLASS_ID不存在!多蹩脚!现在我们建立了外键,如果想插入一个不存在的CLASS_ID,那是会检测出来,报错。这就叫外键约束,也叫完整性约束。
U:上面我们只是修改CLASS表的NAME,那就好办,改了就改了,下次一样通过关联查询查出来。但是,如果我现在的要求是改CLASS表的ID呢?你怎么办?也分两步,一个表改完到另外一个表?卧槽,太麻烦了。外键可以帮到你。有一种完美的结果:在CLASS改完ID后,student中的class_ID跟着改变。就是以前是分两步的,现在一步搞掂!这叫外键的级联操作!含义是操作一次,实现两步。
鸡冻啊,那究竟怎样实现(SQLyog为例)呢?很简单,在设置外键的时候,有以下这么一栏:
这里解释以下这四个radio是神马意思:
层叠:就是上面所有的级联,我最喜欢了,修改了主表(从表外键映射过来的对应字段),注意是主表,从表也会做出相应动作。例如,CLASS_ID更新。
设为Null:就是一旦变动了,student中的CLASS_ID变为null,这个功能在update中应用不大。
无动作(默认):这个就是主表变动了(从表外键映射过来的对应字段),从表不干活,还是保持原来那样。这样的话,外键是作用为0。
约束:这个霸气一点,就是由于主从表示有关联的,所以不允许你主表改变(从表外键映射过来的对应字段)。
我们要实现上面的级联操作,同时更新,要选哪个啊?果断选层叠啊!注意了,推荐在update中的时候选层叠。
现在我们看看sql语句是怎样的。
看到没有,第二行:ON UPDATE CASCADE,on表示触发,就是主表更新(从表外键映射过来的字段)的时候,触发级联事件CASCADE。
D:好了,在说U的时候已经把外键的功能说了一大半了,现在举一反三,要实现上面的删除主表记录,从表的CLASS_ID设为null,也是可以使用外键把两步缩成一步,又是级联操作。
我们该怎么设置?没错,外键选项在删除时设为null就可以了。
有时候是需要主表删除了,对应的从表也要删除,这就要选择层叠了。
呼,写了那么多,好像十分冗杂。现在来总结一下:
1、外键的建立让我们操作数据库更加便捷(当然,这肯定要牺牲效率为前提的)。主要功能有两个:完整性约束(C的例子),级联操作(U,D例子),子表同时变动,依靠事件触发机制。
2、下面做了一个表,直观点看看完整性约束和级联操作的触发情况。
注意:打钩的表示最简易操作,C的时候可以随便你怎样在主表插入数据,不影响从表。U,D的时候怎样操作从表也行,不影响主表。
其实也不用记这个表,就记住一点:外键的建立让我们的多个数据表没有“脏”数据。每条记录都是关联的!
一句话:外键是帮助使用者的一个机制,如果没有外键,约束性和级联操作只能靠使用者较强的逻辑能力了,呵呵,逻辑不强踩了炸弹也不知道。