ASP.NET系统开发(二):漫话级联删除“三剑客”
在中我们所面对的绝大多数都是关联表,表与表之间通过外键相互关联成一个“联盟”,在我们对数据表中的数据进行删除操作时往往会因为外键的作用牵一发而动全身,使得操作失败。说到这我们首先简单的来了解一下表间关联。
如上图中所示的三张表,分别代表的是guojia表(国家),sheng表(省),shi表(市)。在sheng表与guojia表之间,guojia表为主键表,sheng表为外键表,通过将sheng表中的suoshuguojia字段与guojia表中的id字段相互关联将两张表关联起来,表明sheng从属于某一个guojia。同样通过将shi表中的suoshusheng字段与sheng表中的id字段相互关联将两张表关联起来,表明shi从属于某个sheng。这样三张表之间就建立了关联,于是我们希望在删除guojia表中某条记录同时删除属于该国家的省连同属于该省的市的时候便会出现如下错误提示:
消息547,级别16,状态0,第1 行,delete 语句与reference约束"fk_chengshi_guojia"冲突。该冲突发生于数据库"newssystem",表"dbo.sheng",column'suoshuguojia'。语句已终止。
稍懂英文的人都可以看明白这显然是由于外键的作用而导致的删除失败,那么我们究竟怎样才能达到级联删除的目的呢?别着急,下面我就为大家引荐三位剑客来帮助大家解决这个问题:
一、踏雪无痕----触发器
首先我们先来了解一下触大侠的简历:
触发器可以说是一种特殊的存储过程,但它并不像一般存储过程那样供外界调用,它是通过事件来触发的。触发器分为两种,分别是instead of触发器和after触发器。能够引发触发器的触发事件则有三种分别是insert、update和delete事件。after触发器指的是当触发事件发生的时候,先执行该事件,然后再执行预设代码,instead of触发器指的是当触发事件发生后并不执行该触发事件,而是越过触发事件执行预设代码。下面我们就做一个触发器来实现数据的级联删除:
[sql]
ceratetrigger [shanchu]
<span style="white-space:pre"> </span>on [dbo].[category]
<span style="white-space:pre"> </span> instead of delete
as
begin
--声明一个变量
declare @caid int
--给变量赋值
select @caid=id from deleted
--删除市
delete shi where id=(selectsuoshusheng from sheng where suoshuguojia=@caid)
--删除省
delete sheng wheresuoshuguojia=@caid
--删除国家
delete guojia where id=@caid
end
可能有的同学会问,为什么要用instead of触发器而不用after触发器呢?这还得从级联删除的机制说起。当我们执行级联删除操作时,它会一级一级的向下查找,如我们执行语句 delete guojiawhere id=1(删除第一级数据)的时候它会自动向下检查第二级中是否存在属于该国家的省的数据,如果不存在的话它会直接执行删除操作将国家表中的相应数据删除,但是当第二级中存在相应的数据的时候由于外键的作用便不能删除第一级中的数据,同理第二级数据和第三级数据也是一样。由此,我们可以得出一个结论,如果表之间存在外键关联的话,删除上一级数据的前提是下一级中不存在相应数据。
通过上面的讲解我相信大家现在很容易的就能理解为什么用instead of触发器了。原因就在于after触发器是在执行删除操作之后再执行我们所设定的代码的,但是由于外键的作用,根本就不允许执行删除操作,既然根本就不存在执行删除操作那么有何来执行删除操作之后之说呢。instead of触发器就恰好避免了上述问题,由于instead of触发器是越过触发事件来执行预设语句的,所以当删除事件发生的时候并不是执行删除操作,而是执行下面的预设语句来先删除第三级数据,再删除第二级语句最后在删除第一级语句。当下一级中没有数据的时候那上一级的数据自然可以删除了。
二、江南圣手—存储过程
存储过程是一组为了完成特定功能的sql语句集,经编译后存储在数据库中。用户在使用存储过程的时候只要指定存储过程的名字并给出相应的参数就可以了。存储过程是sql语句和可选控制流语句的预编译集合,以一个名字存储并作为一个存储单元。说的更直白一点,存储过程更像是存储在数据库中的一个类,在类的内部封装了属性和方法,使用时,只要给它一个参数便能够执行相应的功能。如果一定要概括一下它的优点,大体可分为以下几点:
1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般sql语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
2.当对数据库进行复杂操作时(如对多个表进行update,insert,query,delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。
3.存储过程可以重复使用,可减少数据库开发人员的工作量
4.安全性高,可设定只有某此用户才具有对指定存储过程的使用权
触发器触大侠可以看成是存储过程的弟子,既然徒弟可以轻而易举的做到级联删除,那么我们来看看师傅是如何处理级联删除的吧:
[sql]
<span style="font-size:12px;">create procedure [dbo].[shanchu]
--声明一个整形变量caid
(@caid int)
as
begin
--删除市
deleteshi where suoshusheng in (select id from sheng where suoshuguojia = @caid )
--删除省
deletesheng where suoshuguojia = @caid
--删除国家
deleteguojia where id=@caid
end</span>
当我们执行这个存储过程的时候只要调用该存储过程并给出参数就完全ok了,其格式为:exec +存储过程名 + 参数。对比一下触发器和存储过程我们可以发现其实质并没有多大的改变,其不同点大致也可以归为这么两点:一是变量声明的位置和形式变了。二是使用过程,触发器是等待事件被触发时属于被动使,而存储过程属于主动调用。
三、卧底隐侠--约束本身
这是在做中最让我感到郁闷的了,sql server 2005以上的版本中都支持数据表的级联删除。我们所需要做的仅仅是对关联关系进行一下设定即可。由于最初没有发现这个功能导致我不得不使用存储过程和触发器,是它隐藏的太深还是我的发现力太差,让人又气又恼。好吧,现在教你轻松搞定级联删除:
1. 新建数据库关系表,添加需要建立外键关联的数据表
2. 将对应的主键表的主键和外键表的某个字段建立外键关系。弹出如下页面:
3.按照上图椭圆处将删除规则设定为“层叠”,按照需要依次将需要建立外键关联的表进行上述设置,无论数据有几级都可以进行彻底删除。
作者:a1314517love