5.5 视图
视图既是表,但又不同于表。它是关系数据库系统提供给用户以多种角度观察数据库中数据的重要机制。视图与表的最大区别:表包含实际的数据,并消耗物理存储;视图不包含真正存储的数据,除了需要存储提供视图定义的查询语句外,不需要其他存储。因此视图也称为虚表,而真正物理存在的表称为基本表或实表。
视图是建立在基本表之上的表,它的结构(所有列的定义)和内容(所有数据行)都来自基本表,它依据基本表的存在而存在。一个视图可以对应一个基本表,也可以对应多个基本表,也就是说,每个视图的列可以来自同一个基本表,也可以来自多个不同的基本表。基本表是实际存在于数据库中的。视图由用户定义,并为用户所使用,视图一经定义,它的操作就如同基本表一样,用户可在基本表上进行操作,也可在视图上进行操作,在视图上进行的操作将由DBMS转化为相应基本表的操作。注意,视图的建立和删除只影响视图本身,不影响对应的基本表,而视图的更新(插入、删除和修改)直接影响基本表,即直接更新对应基本表中的数据。当视图来自多个基本表时,通常只允许对视图进行适当的修改(如对非主属性的修改),不允许进行插入和删除数据的操作。实际上,视图做得最多的是查询,它与基本表的查询操作相同。
对用户而言,视图就如一个用于查看数据的窗口,任何对基本表中所映射的数据更新,通过该窗口在视图可见的范围内都可以实时地和自动地看见。同样,任何对视图的更新也将自动地和实时地在相应基本表中所映射的数据上进行。
1. 定义视图
定义视图的一般语句格式为
CREATE VIEW
<视图名> [(<列名> [,<列名>]…)]
AS <SELECT 子句>
[WITH CHECK OPTION]
其功能为在当前数据库中根据SELECT 子句的查询结果建立一个视图,包括视图的结构和内容。
<视图名>是用户定义的一个标识符,用于表示一个视图。中括号内包含属于该视图的一个或多个由用户定义的列名,每个列名依次与SELECT子句中所投影的每个列相对应,即与对应列的定义和值相同,但列名可以相同也可以不同。
WITH CHECK OPTION是为了防止用户通过视图对数据进行更新时,对不属于视图范围内的基本表数据进行误操作。加上该子句后,当对视图上的数据进行更新时,DBMS会检查视图中定义的条件,若不满足,则拒绝执行。
关系数据库管理系统执行CREATE VIEW语句时,只是把视图定义存入数据字典,并不执行其中的SELECT语句。当查询视图时,按视图的定义从基本表中查出数据。
1)基于基本表的视图
【例5-59】建立计算机系学生信息的视图,视图包含学号、姓名、性别和系名,写出相关语句。
解:CREATE VIEW IS_Student
AS
SELECT sno,sname,sex,department
FROM student
WHERE department='计算机'
这里的视图IS_Student是由其后子查询建立的。如果以后修改了基本表student的结构,则student与IS_Student视图的映像关系被破坏,因而视图IS_Student就不能正常工作了。为了避免出现这类问题,最好在修改基本表之后删除由该基本表导出的视图,然后重建视图。
【例5-60】建立计算机系学生视图,并要求进行修改和插入操作时仍需保证该视图中只有计算机系的学生,写出相关的语句。
CREATE VIEW IS_Student1
AS
SELECT sno, sname, age
FROM student
WHERE department= '计算机'
WITH CHECK OPTION
该语句末尾的WITH CHECK OPTION表示对IS_Student视图进行更新操作时,修改操作自动加上department='计算机'的条件;删除操作自动加上department='计算机'的条件;插入操作自动检查department属性值是否为“计算机”。如果不是,则拒绝该插入操作;如果没有提供department属性值,则自动定义department为“计算机”。
如果视图是从单个基本表导出的,并且只是去掉了基本表的某些行和某些列,但保留了主码,这样的视图称为行列子集视图。例5-59和例5-60产生的视图都是行列子集视图。
2)基于多个表的视图
视图不仅可以建立在一个表上,还可以建立在多个表上。
【例5-61】建立计算机系选修了c1号课程的学生视图,写出相关的语句。
解:CREATE VIEW IS_S1(sno, sname, grade)
AS
SELECT student.sno, sname, grade
FROM student, enroll
WHERE department='计算机' AND
student.sno=enroll.sno AND cno= 'c1'
3)基于视图的视图
视图不仅可以建立在一个或多个表上,还可以建立在一个或多个已经建立好的视图上。
【例5-62】建立计算机系选修了c1号课程且成绩在90分以上的学生视图,写出相关的语句。
解:CREATE VIEW IS_S2
AS
SELECT sno, sname, grade
FROM IS_S1
WHERE grade>=90
4)基于表达式的视图
定义基本表时,为了减少数据库中的数据冗余,表中只存放基本数据。由基本数据经过各种计算派生出的数据,一般是不存储的。由于并不实际存储视图中的数据,所以定义视图时可以根据应用的需要,设置一些派生属性列。这些派生属性列由于在基本表中并不实际存在,把它们称为虚拟列。带虚拟列的视图称为带表达式的视图。
【例5-63】定义一个反映学生出生年份的视图,写出相关语句。
解:CREATE VIEW BT_S(sno, sname, birth)
AS
SELECT sno, sname,2013-age
FROM student
视图中的出生年份是通过计算得到的,其中birth是基本表并不存在的派生列。
5)基于分组的视图
用带有集函数和GROUP BY子句的查询定义的视图称为分组视图。
【例5-64】将学生的学号及其平均成绩定义为一个视图,写出相关语句。
解:假设enroll表中“成绩”列Grade为数字型,则有
CREAT VIEW S_G(sno, gavg)
AS
SELECT sno,AVG(grade)
FROM enroll
GROUP BY sno
2. 删除视图
创建好视图后,若导出此视图的基本表被删除了,则该视图将失效,但视图定义不会被自动删除,除非指定了基本表的级联(CASCADE)删除,故要用删除语句将该视图删除。删除视图语句的一般格式为
DROP VIEW <视图名>﹛CASCADE︱RESTRICT﹜
其功能是删除当前数据库中的一个视图。该语句从数据字典中删除指定的视图定义,如果该视图上还导出了其他视图,则使用CASCADE级联删除语句,把该视图和由它导出的所有视图一起删除。删除基本表时,由该基本表导出的所有视图定义都必须显式地使用DROP VIEW语句删除。使用RESTRICT应确保只有无相关对象,即无其他视图、约束等定义涉及的视图才能被撤销。
【例5-65】删除视图IS_S1,写出相关语句。
解:DROP VIEW IS_S1
执行此语句后,IS_S1将从数据字典中删除。由IS_S1视图导出的IS_S2视图定义虽然仍然在数据字典中,但是该视图已经无法使用了,因此该视图应该同时删除。可以用CASCADE短语一次删除,语句如下:
DROP VIEW IS_S1 CASCADE
3. 查询视图
在定义视图后,就可以像对基本表一样对视图进行查询了,即前面介绍的对表的各种查询操作都可以作用于视图。
数据库管理系统执行对视图的查询时,首先应进行有效性检查,检查查询所涉及的表、视图等是否在数据库中存在,如果存在,则从数据字典中取出查询涉及的视图的定义,将定义中的子查询和用户对视图的查询结合起来,转换成对基本表的查询;然后再执行这个经过修正的查询。这种将对视图的查询转换为对基本表的查询的过程称为视图消解(View Resolution)。
【例5-66】查询在计算机科学系学生的视图中的男同学,写出相关语句。
解:SELECT sno,sname,sex,department
FROM IS_Student
WHERE sex='男'
例5-66利用视图消解转换后的查询语句为
SELECT sno,sname,sex, department
FROM student
WHERE department= '计算机' AND sex='男'
当对一个基本表进行复杂的查询时,可以先对基本表建立一个视图,然后只需对此视图进行查询,这样就不必再写出复杂的查询语句,而将一个复杂的查询转换成一个简单的查询,从而简化了查询操作。
【例5-67】查询选修了c1号课程的计算机科学系学生,写出相关语句。
解:SELECT IS_Student.sno,sname
FROM IS_Student,enroll
WHERE IS_Student.sno =enroll.sno AND enroll.cno= 'c1'
读者可自行写出例5-67利用视图消解法转换后的查询语句。
视图消解法对视图的查询也是有限制的,例如,在有些情况下,视图消解法不能生成正确查询。
【例5-68】在S_AVG视图中查询平均成绩在80分以上的学生学号和平均成绩,写出相关语句。
解:SELECT *
FROM S_AVG
WHERE gavg>=80
S_G视图的定义为
CREATE VIEW S_AVG (sno, gavg)
AS
SELECT sno, AVG(grade)
FROM enroll
GROUP BY sno
利用视图消解法转换后的查询语句如下:
SELECT sno,AVG(grade)
FROM enroll
WHERE AVG(grade)>=80
GROUP BY sno
我们知道,WHERE子句中是不能用集函数作为条件表达式的,因此修正后的查询语句出现了语法错误,正确的查询语句应该是:
SELECT sno,AVG(grade)
FROM enroll
GROUP BY sno
HAVING AVG(grade)>=80
目前,大多数关系数据库系统对行列子集视图的查询均能进行正确转换。但对非行列子集视图(如例5-61)就不能够进行正确转换了,因此应该直接对基本表进行这类查询。
4. 更新视图
目前,大部分数据库产品能够正确地对视图进行数据查询的操作,但还不能对视图进行任意的更新操作,因此视图的更新操作还不能实现逻辑上的数据独立性。
更新视图是指通过视图插入(INSERT)、删除(DELETE)和修改(UPDATE)数据。由于视图是虚表,因此对视图的更新最终是通过转换为对基本表的更新进行的。
为了防止用户通过视图对数据进行插入、删除、修改操作,无意或故意操作不属于视图范围内的表的数据,可在定义视图时加上WITH CHECK OPTION子句,这样在对视图进行更新操作时,数据库管理系统会进一步检查视图定义中的条件,若不满足条件,则拒绝执行该操作。注意,需要对视图进行修改时,修改由基本表中非主属性所对应的列,若要修改主属性所对应的列,最好到各自的基本表中去修改,以便更好地满足关系规范化和完整性的要求。
【例5-69】为计算机系增加一个新生,其中学号为95024,姓名为赵伟,性别为女,写出相关语句。
解:INSERT INTO IS_student
VALUES('95024','赵伟','女','计算机')
系统在执行此语句时,首先从数据字典中找到IS_student的定义;然后把此定义和插入操作结合起来,转换成等价的对基本表student的插入,该语句相当于执行以下操作:
INSERT INTO student(sno,sname,sex,department)
VALUES('95024','赵伟','女','计算机')
【例5-70】删除赵伟的信息,她的学号是95024,写出相关语句。
解:DELETE FROM IS_student
WHERE sno='95024'
该语句转换为基本表的操作如下:
DELETE FROM student
WHERE department='计算机' AND sno='95024'
【例5-71】将丁晓春的所在系改为电子系,写出相关语句。
解:UPDATE IS_student
SET department='电子'
WHERE sname='丁晓春'
同样,该语句转换成对基本表的操作为
UPDATE student
SET department='电子'
WHERE department='计算机' AND sname='丁晓春'
在关系数据库中,并不是所有的视图都是可更新的,因为有些视图的更新不能唯一地有意义地转换成对相应基本表的更新。
例如,前面定义的视图S_AVG是由学号和平均成绩两个属性列组成的,其中平均成绩一项是由enroll表中对元组分组后计算平均值得来的。若想把视图中某学生的平均成绩改为80分,其更新语句如下:
UPDATE S_AVG
SET AVG(grade)=80
WHERE sname='丁晓春'
这个视图的更新是无法转换成对基本表enroll的更新的,因为系统无法修改各科成绩使它的平均成绩为80分,所以S_AVG视图是不可更新的。
由于对视图的更新最终将转化为对基本表的更新,因此对视图的更新通常要加以限制。对视图更新时,不同的系统其限制程度是不一样的,但一般有如下的限制:
(1) 若视图是由两个以上的表导出的,则此视图不允许更新。
(2) 若视图的列来自表达式或常数,则不允许对此视图执行INSERT或UPDATE操作,但允许执行DELETE操作。
(3) 若视图定义中用到GROUP BY子句或聚集函数的视图,则不允许更新。
(4) 若视图定义中含有DISTINCT,则此视图不允许更新。
(5) 若视图定义中有嵌套查询,并且内层查询的FROM子句涉及的表也是导出该视图的基本表,则此视图不允许更新。
(6) 建立一个不允许更新的视图上的视图不允许更新。
5. 视图的作用
视图是定义在表之上的,对视图的一切操作最终也要转换为对表的操作,而且对视图进行更新时还有可能会出现问题。既然如此,为什么还要定义视图呢?这是因为一个规范化的关系数据库由许多个表组成,而每个表只适合于一类特定的人和事务,通常这些规范化的表需要重新连接起来以便为特定情形创建有意义的信息。视图通过SQL创建必要的连接,查询所需的表的列信息及建立行选择条件来满足特殊情形对数据库的使用要求。通过视图可以定制表的结果集来满足不同用户的特殊需求。而且视图的定义是存储在数据库中的,是标准化的,所有用户对视图的访问都是一样的,使用视图也是很简单的,所以对用户或应用程序来说,用视图比让自己来完成复杂的查询要方便得多。
使用视图的优点如下。
(1) 视图能简化用户的操作。
视图可使用户将注意力集中放在所关心的数据上。如果这些数据不是直接来自表,则可以通过定义视图,使数据库看起来结构简单、清晰,并且可以简化用户的数据查询操作。例如,视图可以将需要的若干基本表连接起来,这样用户只要对视图(虚表)进行简单查询即可,而这个虚表是怎样得来的,用户可以不必了解。
(2) 视图能使用户以多种角度看待同一数据。
视图支持多用户,同时以不同的方式对相同的数据进行查询。同一个数据库中,不同的用户所关心的数据是不同的,用户可以通过视图来查看自己所需的数据并对其进行相应的操作,大家可以同时与同一个数据库进行交互。这种灵活性对用户来讲是非常重要的。
(3) 视图对隐藏的数据自动提供安全保障。
由于视图是基本表导出的,它用于从一个表或多个表的连接结果中选择特殊用户所需的数据,而不需要考虑视图之外的数据。因此,基本表中的某些数据对视图来说可能就是不可见的。所谓“隐藏的数据”是指在某个视图中不可见的数据。显然,这些数据对特定的视图来说,在存取中是安全的,它的安全性通过对视图权限的控制来完成。
(4) 视图提供了一定程度的数据的逻辑独立性。
数据的物理独立性是指用户和用户程序不依赖于数据库的物理结构。逻辑独立性是指当数据库重构时,有些表结构的变化,如增加新的关系或对原有关系改变其结构等,用户和用户程序不会受影响。因此,视图对重构数据库提供了一定程度的逻辑独立性。
在关系数据库中,数据库的重构是不可避免的。最常见的重构数据库是将一个表“垂直”分成多个表。例如,将student(sno,sname,ssex,sage,sdept)分为SX(sno,sname,sage)和SY(sno,ssex,sdept)两个关系。这时原表student为表SX和表SY自然连接的结果,可以建立一个视图student:
CREAT VIEW S(sno,sname,ssex,sage,sdept)
AS
SELECT SX.sno,SX.sname,SY.ssex,SX.sage,SY.sdept
FROM SX,SY
WHERE SX.sno=SY.sno
这样,虽然数据库的逻辑结构发生了变化,由student表变成了SX表和SY表,但由于定义了一个和原表逻辑结构一样的视图,使用户的外模式没有发生变化,因此,用户的应用程序不必修改,仍可通过视图来查询数据,但更新操作可能会受到影响。
视图只能在一定程度上提供数据的逻辑独立性。例如,对视图的更新是有条件的,因此应用程序中修改数据的语句可能仍会因表结构的变化而变化,所以数据的更新操作还不能完全实现逻辑上的数据独立性。