SQL Server中更新视图, 可能出现的错误及处理
定义视图后,对视图的查询没有什么限制,可以像对待表一样进行操作。但是,如果对视图中的元组进行更新操作(INSERT,UPDATE,DELETE)将受到限制。概括起来,关于可更新视图有以下三条规则:
(1) 若视图是基于多个表使用联接操作而导出的,那么对这个视图执行更新操作时,每次只能影响其中的一个表。
(2) 若视图导出时包含有分组和聚合操作,则不允许对这个视图执行更新操作。
(3) 若视图是从一个表经选择、投影而导出的,并在视图中包含了表的主键字或某个候选键,这类视图称为‘行列子集视图’。对这类视图可执行更新操作。
定义可更新视图时加上WITH CHECK OPTION短语,表示强制在视图上的所有数据更新语句都必须符合由select查询语句所设置的准则。
由于视图不一定包括表中的所有字段,所以在插入记录时可能会遇到问题。视图中那些没有出现的字段无法显式插入数据,假如这些字段不接受系统指派的null值,那么插入操作将失败。但这类视图仍然可以用于修改和删除操作。
·设有下面的视图定义:
use mydb go CREATE VIEW s_e_c_view as SELECT student.sno,sname,cname,grade FROM student,elective,course WHERE student.sno=elective.sno and elective.cno=course.cno
这个视图由三个表联接而成。如果对该视图执行下列插入操作:
use mydb INSERT INTO s_e_c_view VALUES ( 200200130, ’张小冬’, ’english’, 82 )
系统将发出错误信息:“视图或函数‘s_e_c_view’不可更新,因为修改会影响多个基表”。我们不妨假设在表course中,一个非键字段值(课程名cname)可能对应多个主键值(课程号cno)。现在只有课程名‘english’而主键课程号cno不确定,显然不能把数据插入course表中。同样,由于cno不确定,也不能把成绩82插到elective表中。因此,不允许对这个视图执行插入操作。这里的s_e_c_view视图属于规则(1)所述的情形。
如果把插入操作换成修改操作,而且只影响其中一个表,同时新数据值中含有主键字,系统将接受这个修改操作。实例如下:
use mydb
UPDATE s_e_c_view SET sno=200200130, sname='张小冬' WHERE sno=200200102
·设有下面的视图定义:
use mydb go CREATE VIEW e_view ( sno, c_amounnt, avg_grade ) as SELECT sno,count(cno),avg(grade) FROM elective WHERE grade is not null GROUP BY sno
这个视图虽然仅从一个表中导出,但导出时使用了分组和聚合操作。如果对该视图执行下列插入操作:
use mydb
INSERT INTO e_view VALUES ( 200200120, 2, 78 )
系统将发出错误信息:“视图或函数‘e_view' 不可更新,因为它包含聚合”。
事实上,在原来的elective表中根本就不存在‘修课总数’和‘平均成绩’两列。上述的更新操作显然不切实际。因此,不允许对这个视图执行更新操作是合理的。这里的e_view视图属于规则(2)所述的情形。
·设有下面的视图定义:
use mydb go
CREATE VIEW s_view as SELECT sno,sname,height FROM student WHERE sex=’男’
这个视图基于一个表且只使用选择和投影操作,同时还包含了主键字sno,所以这个视图是可更新的。如果对该视图执行下列插入操作:
use mydb
INSERT INTO s_view VALUES ( 200200120, '黄大春', 178 )
在student表中将插入一个新的记录,对记录中没有指定值的字段系统将指派一个NULL值。这里的s_view视图属于规则(3)所述的情形。
关于可更新视图的一些更具体的描述如下。
如果视图没有INSTEAD OF触发器,或者视图不是分区视图,则视图只有满足下列条件才可更新:
select语句在选择列表中没有聚合函数,也不包含TOP,GROUP BY,UNION(除非视图是分区视图)或DISTINCT子句。聚合函数可以用在FROM子句的子查询中,只要不修改函数返回的值。
select语句的选择列表中没有派生列。派生列是由任何非简单列表达式(使用函数、加法或减法运算符等)所构成的结果集列。
select语句中的FROM子句至少引用一个表。select语句不能只包含非表格格式的表达式(即不是从表派生出的表达式)。
INSERT,UPDATE和DELETE语句在引用可更新视图之前,也必须如上述条件指定的那样满足某些限制条件。只有当视图可更新,并且所编写的UPDATE或INSERT语句只修改视图的FROM子句引用的一个基表中的数据时,UPDATE和INSERT语句才能引用视图。只有当视图在其FROM子句中只引用一个表时,DELETE语句才能引用可更新的视图。