SQL查询的艺术
上QQ阅读APP看书,第一时间看更新

第4章 索引与视图的创建

和表一样,索引和视图也是数据库中的重要对象。本章将介绍索引、视图的概念及其创建与管理。

4.1 索引的基础知识

用户对数据库最频繁的操作是数据查询。一般情况下,数据库在进行查询操作时,需要对整个表进行搜索。当表中的数据很多时,搜索数据就需要很长的时间,这就造成了服务器的资源浪费。为了提高检索数据的能力,数据库引入了索引机制。

4.1.1 索引的概念

索引是一个单独的、物理的数据库结构,是数据库的一个表中所包含的值的列表,其中注明了表的各个值所在的存储位置。索引是依赖于表建立的,提供了编排表中数据的方法。

实际上,一个表的存储是由两部分组成的,一部分用来存放表的数据页面,另一部分存放索引页面,索引就存放在索引页面上。通常,索引页面相对于数据页面来说小得多。当进行数据检索时,系统先搜索索引页面,从中找到所需数据的指针,再通过指针从数据页面中读取数据。

从某种程度上,我们可以把数据库看作一本书,把索引看作书的目录,通过目录查找书中的信息。显然,与没有目录的书相比,这显得方便和快捷。

4.1.2 索引的结构

一般的数据库,如SQL Server、Oracle等,按存储结构的不同将索引分为两类,簇索引(ClusteredIndex)和非簇索引(Nonclustered Index)。

1.簇索引

簇索引对表的物理数据页中的数据按列进行排序,然后再重新存储到磁盘上,即簇索引与数据是混为一体的,它的叶节点中存储的是实际的数据。

簇索引对表中的数据一一进行了排序,因此用簇索引查找数据很快。但由于簇索引将表的所有数据完全重新排列了,所需要的空间也就特别大,大概相当于表中数据所占空间的120%。

表的数据行只能以一种排序方式存储在磁盘上,所以一个表只能有一个簇索引。

注意

例如,在学生信息表中,为学号字段建立簇索引,索引的典型结构如图4-1所示。

图4-1 簇索引的结构

2.非簇索引

非簇索引具有与表的数据完全分离的结构,使用非簇索引不必将物理数据页中的数据按列排序。非簇索引的叶节点中存储了关键字的值和行定位器。行定位器的结构和存储内容取决于数据的存储方式。如果数据是以簇索引方式存储的,则行定位器中存储的是簇索引的索引键;如果数据不是以簇索引方式存储的,则行定位器存储的是指向数据行的指针,这种方式又称为堆存储方式(Heap Structure)。非簇索引将行定位器按关键字进行排序,这个顺序与表的行在数据页中的排序是不匹配的。

由于非簇索引使用索引页存储,比簇索引需要更多的存储空间,且检索效率较低。但一个表只能建一个簇索引,当用户需要建立多个索引时,就需要使用非簇索引了。从理论上讲,一个表最多可以建249个非簇索引。

同样,在学生信息表中,为学号字段建立非簇索引,索引的典型结构如图4-2所示。

图4-2 非簇索引的结构

4.2 索引的创建与销毁

在SQL中,创建索引是由CREATE INDEX关键字实现的。但在不同的数据库管理系统中,CREATE INDEX语句有不同形式的扩展。索引的销毁则是由DROP INDEX关键字实现的。

4.2.1 基本创建语法

创建素引的基本关键字为CREATE INDEX,在其后要指明创建索引的名称,并需要指明表的名字及创建索引的列。语法如下。

    CREATE INDEX index_name
    ON table_name(column_name1, [column_name2], ...);

每个索引必须有惟一的名字。ON关键字后面为创建索引的表的名字,在括号内列出索引包含的列(当然,可以为多列)。

在不同的数据库管理系统中,创建索引语句有着不同形式的扩展。如在SQL Server中,CREATE INDEX语句创建索引可以有如下的形式。

    CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name
    ON { table | view } ( column [ ASC | DESC ] [ ,...n ] )
    [ WITH { PAD_INDEX | FILLFACTOR = fillfactor | IGNORE_DUP_KEY |
    DROP_EXISTING | STATISTICS_NORECOMPUTE | SORT_IN_TEMPDB} [ ,...n] ]
    [ ON filegroup ]

在Oracle中,CREATE INDEX语句创建索引可以有如下的形式。

    CREATE INDEX [schema.]index
    ON { [schema.]table (column [!!under!!ASC|DESC]
        [, column [!!under!!ASC|DESC]] ...)
      | CLUSTER [schema.]cluster }
    [INITRANS integer] [MAXTRANS integer]
    [TABLESPACE tablespace]
    [STORAGE storage_clause]
    [PCTFREE integer]
    [NOSORT]

在Informix中,CREATE INDEX语句创建索引可以有如下的形式。

    CREATE [UNIQUE | DISTINCT] [CLUSTER] INDEX index_name
    ON table_name (column_name [ASC|DESC],column_name [ASC|DESC]...)

用到的几个主要关键字含义如下。

UNIQUE(DISTINCT):惟一性索引,不允许表中不同的行在索引列上取相同值。若已有相同值存在,则系统给出相关信息,不建此索引。

CLUSTERED/ NONCLUSTERED:聚集和非聚集索引,若为CLUSTERED,则为聚集索引,即表中元组按索引项的值排序,并聚集在一起。一个基本表上只能建一个聚集索引。NONCLUSTERED表示创建的索引为非聚集索引。缺省时,创建的索引为非聚集索引。

ASC/DESC:索引表中索引值的排序次序,缺省为ASC(正序排列)。

关于其他关键字和语句的含义,读者可参阅各数据库系统的参考手册。所有这些创建形式有一些共同点,即都包含了基本的创建语句:

    CREATE INDEX index_name
    ON table_name (column_name, ...)

本章介绍的索引操作就是围绕这个基本创建语句展开的。

4.2.2 本章实例用到的实例表

本章介绍索引和视图操作实例,共用到了3个实例表,分别为学生信息表(StudentInfo)、招生信息表(RecruitInfo)和院系信息表(Department)。

1.学生信息表(StudentInfo)

StudentInfo表包含学号(sno)、姓名(sname)、性别(sex)、生源(address)、系号(dno)几个字段信息。该表的结构及数据如表4-1所示。

表4-1 StudentInfo表的结构及数据

该表的SQL生成代码如下。

    CREATE TABLE   StudentInfo
    (
        sno     char (3)   NOT NULL ,
        sname   char (8)   NOT NULL ,
        sex     char(2)   NOT NULL ,
        address  char(8)   NOT NULL ,
        dno     int     NOT NULL
    )
    INSERT INTO  StudentInfo  VALUES('001','张平','女', '湖南', 2)
    INSERT INTO  StudentInfo  VALUES('002','李山','男', '北京', 4)
    ……

2.招生信息表(RecruitInfo)

RecruitInfo表包含生源(address)、录取分数(score)和招收人数(snum)3个字段,其结构及数据如表4-2所示。

表4-2 RecruitInfo表的结构及数据

该表的SQL生成代码如下。

    CREATE TABLE   RecruitInfo
    (
        address  char(10)    NOT NULL,
        score    foat        NOT NULL,
        snum     int         NOT NULL
    )
    INSERT INTO  RecruitInfo  VALUES(‘北京’,560, 220)
    INSERT INTO  RecruitInfo  VALUES(‘湖南’,648.5, 65)
    ……

3.院系信息表(Department)

Department表包含系号(dno)、系名(dname)和招收人数(dnum)3个字段,其结构及数据如表4-3所示。

表4-3 Department表的结构及数据

该表的SQL生成代码如下。

    CREATE TABLE   Department
    (
        dno          int             NOT NULL,
        dname        char(20)        NOT NULL,
        dnum         int             NOT NULL
    )
    INSERT INTO  Department  VALUES(1,'计算机工程系', 220)
    INSERT INTO  Department  VALUES(2,'汽车系', 80)
    ……

4.2.3 创建简单的非簇索引

当一个表的记录数很大时,为查询符合条件的记录,扫描整个表要花费很长时间。如在StudentInfo表中查询学生“吴军”的记录,查询语句如下。

    SELECT * FROM StudentInfo WHERE sname=’吴军’

假如StudentInfo表中有100000条记录,为查询“吴军”学生的记录,我们就需要用WHERE条件对100000条记录逐一进行核对,显然效率低下,而此时如果对sname字段建立了索引,该索引如同对sname字段内的所有记录进行了某种排序,通过分析姓名“吴军”,很快就会定位到它在表中的记录位置,从而提高了检索效率。

下面我们通过一个实例介绍一下非簇索引的创建和使用过程。该实例将为StudentInfo表中的姓名(sname)字段创建非簇索引Name_Index。

1.索引的创建

索引创建前,我们首先要查看学生信息表(StudentInfo)中的数据。

实例1 查询创建索引前StudentInfo表中的数据

实例代码如下。

    SELECT * FROM  StudentInfo

运行结果如图4-3所示。

图4-3 学生信息表

实例2 为StudentInfo表中的sname字段创建非簇索引

实例代码如下。

    CREATE  INDEX  Name_Index
    ON  StudentInfo (sname )

执行该代码,系统会提示索引创建成功。在非簇索引中,DBMS只对创建索引的列的键值进行排序(升序),而索引的表行不排序。

实例3 查询索引的键字值

实例代码如下。

    SELECT      sname
    FROM        studentinfo

运行结果如图4-4所示。

图4-4 索引的键字值

由此可见,索引的键值按照升序进行了排序。

实例4 查询创建索引后StudentInfo表中的所有数据

实例代码如下。

    SELECT * FROM  StudentInfo

运行结果如图4-5所示。

图4-5 创建索引后的学生信息表

结果与创建索引前的查询结果没有什么变化。这是因为这里创建的是非簇索引。它并没有改变表中数据存放的物理位置。

2.非簇索引的使用

由于大多数数据库系统具有使用多个索引的能力,如SQL Server,当在表上创建一个或多个索引后,SQL Server的查询优化器会自动决定在查询执行期间使用哪个索引。

为了在SQL Server中使用索引,WHERE子句中的列之一必须是索引所在列,对于本例来说就是sname列。

当然也可以不让SQL Server的查询优化器自动决定索引,而是强制使用某种索引,语法如下。

    SELECT      column1,column2,……
    FROM            table_name
    WITH        (INDEX (index_name))
    WHERE       condition

index_name指明了要使用的索引名字。

实例5 强制使用非簇索引查询表

强制使用Name_Index索引,查询学生信息表(StudentInfo)中的数据,代码如下。

    SELECT      *
    FROM        StudentInfo
    WITH        (INDEX (Name_Index))

运行结果如图4-6所示。

图4-6 强制索引使用的检索结果图

对比使用索引前后数据库表中的数据,我们可以发现,数据按照sname字段升序的方向重新排列。因为这里强制使用了Name_Index索引,所以该索引中,sname字段是按照记录升序的方向排列的。

说明

对于查询表中的所有记录信息,使用索引是毫无意义的。这里这么做是为了演示索引的作用效果,下同。

在SQL的SELECT语句中,使用ORDER BY关键字同样可以对查找结果进行排序。

实例6 使用ORDER BY关键字对查找结果排序

实例代码如下。

    SELECT * FROM  StudentInfo
    ORDER BY  sname

运行结果如图4-7所示。

图4-7 按照姓名排序的结果

说明

有关Select语句,后面的章节中会有详细的介绍。

虽然从表面上看,使用SELECT……ORDER BY语句得到了相同的结果,但是,二者从本质上有很大的区别。ORDER BY关键词在每次查询数据时,都要对数据进行排序;而创建索引后,数据库系统实际上创建了一个索引结构体,用户每次使用查询数据时,都使用相同的索引结构,从而节约了时间。

注意

当数据库表被删除时,和它相关的所有索引都将被删除。

4.2.4 多字段非簇索引的创建

SQL允许用户在一个表中,在两个或多个字段上创建多字段索引,这种索引又被称为复合索引。有时,建立这类索引在实际应用中也是必要的。

例如,在学生选课表中,我们经常要查询某某同学(如张三)选修的某门课程(如数学)的成绩。这时,如果只在一个字段上建立索引,则查询效率要低些。比如,只在学生姓名字段上建立索引,执行查询时,系统将利用索引找出张三同学的所有选课记录,然后再对课程逐一扫描,找到课程为数学的记录;而如果只在课程字段上建立索引,执行查询时,系统将利用索引找出所有选修数学课程的学生记录,再对这些记录的姓名字段进行逐一扫描,找到姓名为张三的记录。这时,为了提高查询效率,我们可以同时为姓名和课程两个字段建立一个索引。

说明

因为学生数一般远远大于课程数,所以如果只在一个字段上建立索引,以建姓名字段为好。

实例7 创建并强制使用多字段索引检索表

为StudentInfo表中的性别(sex)和姓名(sname)字段创建索引SexName_Index。

实例代码如下。

    CREATE  INDEX  SexName_Index
    ON  StudentInfo (sex , sname )

强制使用SexName_Index索引,查询StudentInfo表中的所有记录。

    SELECT  *
    FROM        StudentInfo
    WITH        (INDEX (SexName_Index))

运行结果如图4-8所示。

图4-8 强制使用索引检索结果图

我们可以发现,在创建的索引中,sex字段的优先级要高于sname字段。在创建多字段索引时,各字段的排列顺序决定了其优先级,排列越靠前,具有越高的优先级。

4.2.5 使用UNIQUE关键字创建惟一索引

惟一索引是指不允许在两行中存在相同的索引值。惟一索引可以拥有一行或者多行。如果用户试图使用INSERT或UPDATE语句,在拥有惟一索引的数据中生成一个重复的值,那么INSERT或者UPDATE就会被终止,SQL服务器会生成一个错误信息。

实例8 创建并强制使用惟一索引检索表

为StudentInfo表中的学号(sno)字段创建惟一索引Sno_Index,且指定降序排序。

    CREATE  UNIQUE  INDEX  Sno_Index
    ON  StudentInfo (sno DESC )

强制使用Sno_Index索引,查询StudentInfo表中的所有记录。

    SELECT  *
    FROM        StudentInfo
    WITH        (INDEX (Sno_Index))

运行结果如图4-9所示。

图4-9 强制使用索引检索所有的记录

如果用户要向StudentInfo表中插入具有相同学号的学生信息,如:

    insert  StudentInfo  (sno, sname, sex, address, dno)
    values  ('006','刘备','男','河南',1)

SQL Server会报错,在查询分析器的Result窗格将显示如下信息。

    Cannot insert duplicate key row in object 'StudentInfo' with unique index
    'Sno_Index'.
    The statement has been terminated.

注意

当创建惟一索引时,我们应确保被索引的列不允许NULL值。例如,SQL Server将NULL也看作是一个值,如果创建惟一索引的列允许NULL值,假定原先该列不存在NULL值,那么向其中插入NULL值时,第一个是合法的,第二个就会失败。因为两个NULL值被视为违背惟一性原则。

当然,创建惟一性索引的前提是,创建索引的列中已有的记录本身没有重复的值。否则,系统会报错,创建失败。

实例9 具有重复值字段的惟一性索引的创建

为StudentInfo表中的address字段创建惟一索引Address_Index。

    CREATE  UNIQUE  INDEX  address_Index
    ON  StudentInfo (address )

运行结果如下。SQL Server数据库系统会报错,在查询分析器的Result窗格将显示如下信息。

    CREATE UNIQUE INDEX terminated because a duplicate key was found for index ID
    4. Most significant primary key is '山东     '.
    The statement has been terminated.

这是因为在StudentInfo表的address列中,其值不是惟一的(包含两个“山东”记录),因此对其创建惟一性索引将导致创建失败。

4.2.6 使用CLUSTERDE关键字创建簇索引

与非簇索引不同,簇索引改变了表中数据存放的物理位置。在带有簇索引的表中,行是以索引顺序存放的。簇索引不仅对索引中的键字值进行排序,而且对表中的行排序,以便使其与索引的排序相匹配。使用簇集索引主要有三点优势。

使用簇集索引的表将占用最小的磁盘空间。因为DBMS在插入新行时,会自动地重

用以前分配给删除行的空间。

对基于簇集索引的列值进行查询时,会有更快的执行速度,因为所有值在物理磁盘上相互靠近。

基于簇集索引的列以升序显示数据查询,不再需要ORDER BY子句,因为表的数据本身已经按所要求的输出顺序排列。

当检索带有连续键值的多行时,如查询姓王的所有学生时,簇索引就显示出很多优势。一旦找到了第一个键值,后续索引值的行必定物理地排在后面,这样就无需进一步访问磁盘了。

实例10 创建单字段簇索引并查询创建索引后的表

为StudentInfo表中的姓名(sname)字段创建簇索引Snamecluseter_Index。

    CREATE  CLUSTERED  INDEX  Snamecluseter_Index
    ON  StudentInfo (sname )

此时查询学生信息表(StudentInfo)中的数据:

    SELECT * FROM  StudentInfo

运行结果如图4-10所示。

图4-10 创建单字段簇索引后的表记录

由此可见,创建簇索引后,表中数据(行)存储的物理位置发生了变化,与非簇索引一样,也可以建立多字段簇集索引。

实例11 创建多字段簇索引

为StudentInfo表中的sex和address字段创建簇索引SexAddressCluseter_Index。当然,建立以前我们要删除已有的簇集索引Snamecluseter_Index。

    DROP INDEX studentinfo.Snamecluseter_Index
    CREATE  CLUSTERED  INDEX  SexAddressCluseter_Index
    ON  StudentInfo (sex , address )

此时,查询StudentInfo表中的所有记录:

    SELECT      *
    FROM        StudentInfo

运行结果如图4-11所示。

图4-11 创建多字段簇索引后的表记录

我们可以发现,在创建的多字段簇集索引中,sex字段的优先级要高于address字段。

注意

一个表中只能创建一个簇索引。因为表行必须以簇集索引的顺序排列,而单个表在磁盘上只能有一个物理记录排列方式。

在对表创建了一个簇集索引之后,在插入新行或是更新簇集索引一部分的列值时,DBMS将自动地重新排序。因此,对那些经常大量插入行或更新索引列值的表,我们尽量不要建立簇集索引。

4.2.7 索引的销毁

在SQL中,无论是簇集索引还是非簇索引,都可以通过DROP INDEX关键字销毁。语法如下。

    DROP  INDEX  index_name

销毁索引,我们只需在DROP INDEX关键字后写入索引的名称即可。

实例12 销毁索引

将为StudentInfo表中的sname字段创建的索引Name_Index销毁。

    DROP  INDEX  StudentInfo.Name_Index

注意

在SQL Server中,用DROP INDEX语句销毁索引时,我们一定要指明索引所在表的名字,如实例代码中的StudentInfo.Name_Index。

4.2.8 使用索引的几点原则

前面我们介绍了索引的创建和销毁。在一个表中,什么时候需要创建索引,创建索引又需要注意些什么呢?下面是创建索引时,需要注意的几点。

对小的数据表来说,使用索引并不能提高检索效率,因此不需对其创建索引。

当用户要检索的字段的数据包含有很多数值或很多空值(NULL)时,为该字段创建索引,会大大提高检索效率。

当用户查询表中的数据时,如果查询结果包含的数据(行)较少,一般少于数据总数的25%时,使用索引会显著提高查询效率。反之,如果用户的查询操作返回结果总是包含大量数据,那么索引的用处不大。

索引列在WHERE子句中应频繁使用。例如,我们在学生姓名字段上建了索引,但实际查询中并不是经常用姓名作为查询条件,该索引就没有发生作用。

我们要先装数据,后建索引。对于大多数的表,总有一批初始数据需要装入。该原则是说,建立表后,我们要先将这些初始数据装入表,然后再建索引,这样可以加快初始数据的录入。如果建表后就建索引,那么在输入初始数据时,每插入一个记录都要维护一次索引;当然,对于索引来说,早建和晚建都是允许的。

索引提高了数据检索的速度,但也降低了数据更新的速度。如果要对表中的数据进行大量更新,我们最好先销毁索引,等数据更新完毕再创建索引,这样会提高效率。

索引要占用数据库空间。在设计数据库时,我们要把需要的索引空间考虑在内。

我们要尽量把表和它的索引存放在不同的磁盘上,这样会提高查询速度。

4.3 视图的基础知识

视图是从一个或多个表中查询数据的另外一种方式。利用视图,用户可以集中、简化、定制数据库,同时还可以提供安全保证。

4.3.1 视图简介

视图是从一个或多个表中导出的表,其结构和数据是建立在对表的查询基础上的。和表一样,视图也是包括几个被定义的数据列和多个数据行,但就本质而言,这些数据列和数据行来源于它所引用的表。所以视图不是真实存在的基础表,而是一张虚表。

视图所对应的数据并不以视图结构存储在数据库中,而是存储在视图所引用的表中。关于视图的概念如图4-12所示。

图4-12 视图的基本示意图

通过视图看到的数据只是存放在基本表中的数据。对视图的操作与对表的操作一样,我们可以对其进行查询、修改(有一定的限制)和删除。

当我们对视图中的数据进行修改时,相应的基本表数据也要发生变化;同时,如果基本表的数据发生变化,那么这种变化也可以自动地反映到视图中。

4.3.2 视图的优缺点

视图有很多优点,主要表现在简化操作、定制数据、合并分隔数据、安全性等方面。

简化操作。视图大大简化了用户对数据的操作。在定义视图时,视图本身就可以是一个复杂查询的结果集。因此,在每一次执行相同的查询时,不必重新写这些复杂的查询语句,只要一条简单的查询视图语句即可。

定制数据。视图能够让不同的用户、以不同的方式看到不同或相同的数据集。因此,

当有许多不同水平的用户共用同一数据库时,这显得极为重要。比如,我们想让公司的用户访问某些职员记录,但不想让这些用户获得诸如医疗卡号或工资之类的信息,那么就可以创建一个视图,只为他们提供应该看到的信息。

合并分隔数据。在有些情况下,由于表中数据量太大,在表的设计时,常将表进行水平分割或垂直分割,但表的结构的变化却会对应用程序产生不良的影响。使用视图就可以重新保持原有的结构关系,从而使外模式保持不变,原有的应用程序仍可以通过视图来重载数据。

安全性。视图可以作为一种安全机制。通过视图,用户只能查看和修改他们所能看到的数据。其他数据库或表既不可见,也不可以访问。如果某一用户想要访问视图的结果集,必须授予其访问权限。视图所引用表的访问权限与视图权限的设置互不影响。

使用视图主要有两个缺点。

性能。由于视图是虚拟的表,在使用包括视图引用的SQL语句时,数据库除了执行所键入的SQL语句中的查询或更新之外,还要告诉DBMS执行定义视图的查询,这就影响了查询效率。

更新限制。不是所有的视图都是可更新的。目前,SQL将可更新的视图限制为基于对单个表的,并且没有GROUP BY或者HAVING子句的查询。除此之外,为了使视图是可更新的,视图不能使用聚集函数、计算的列或SELECT DISTINCT子句。

由于SQL对更新视图的限制,用户不能总是用视图来代替表。另外,在使用视图的情况下,我们要综合考虑使用视图的优势和DBMS每次执行创建视图的SQL语句引起的性能损失。

4.4 视图的创建与销毁

与前面介绍的创建表、创建索引相似,视图的创建主要是由CREATE VIEW关键字实现的,视图的销毁则是由DROP VIEW关键字实现的。

4.4.1 基本创建语法

视图的创建主要由CREATE VIEW关键字实现,其数据则由SELECT语句定义。语法如下。

    CREATE VIEW <view_name> [(column1, column2...)]
    AS
    SELECT <column_names>
    FROM <table_name>

[(column1, column2...)]为可选项,缺省时,为子查询结果中的字段名。SELECT语句指明了视图中的字段及其数据。关于视图我们还要强调两点。

视图创建后,在数据字典中只存放视图的定义,而其中的SELECT语句并不执行。

只有当用户对视图进行操作时,才按照视图的定义将数据从基本表中取出。

4.4.2 创建简单的视图

本节将给出几个实例,说明几种简单视图的创建。

1.创建与表具有相同信息的视图

实例13 为整表创建视图并查看其信息

创建一个与StudentInfo表具有相同信息的视图StudentInfo_View。

    CREATE VIEW StudentInfo_View
    AS
    SELECT * FROM StudentInfo

查看视图的数据。

    SELECT *
    FROM  StudentInfo_View

运行结果如图4-13所示。

图4-13 视图的数据信息

我们可以发现,视图StudentInfo_View中的信息与StudentInfo表完全相同(因为前面为StudentInfo表创建了簇索引,所以物理存储位置发生了变化)。

2.为视图创建视图

实际上,我们也可以把视图看成是一个表,还可以为视图创建视图。

实例14 为视图创建视图并查看其信息

为视图StudentInfo_View包含的所有男同学的信息创建一个视图Boy_View。

实例代码如下。

    CREATE VIEW  Boy_View
    AS
    SELECT * FROM  StudentInfo_View
    WHERE  sex = '男'

查看视图的数据。

    SELECT *
    FROM  Boy_View

运行结果如图4-14所示。

图4-14 视图的视图数据信息

注意

由于视图是一个“虚表”,当表被删除时,由该表创建的视图,或视图的视图都不可用。

3.为表中的一列或者几列信息创建视图

实例15 为列创建视图并查看其信息

为StudentInfo表中的姓名(sname)、生源(address)信息创建一个视图NameAddress_View。实例代码如下。

    CREATE VIEW  NameAddress_View
    AS
    SELECT sname, address
    FROM  StudentInfo

查看视图的数据。

    SELECT *
    FROM  NameAddress_View

运行结果如图4-15所示:

图4-15 列视图的信息

注意

用户可以通过创建视图进行数据查询。例如,一个表有50列,有成千上万行,而用户只需要使用表中的两列数据,这时,我们可以为这两列创建一个视图,在视图中查询需要的数据,这样会大大提高查询效率。

4.创建与表具有不同字段名的视图

前面实例创建的视图,并没有特别指明视图的字段名,系统就默认为与表相同的字段名。实际上,创建视图时,我们也可以为表中的数据定义新的字段名。

实例16 为表创建具有不同字段名的视图并查看其信息

为StudentInfo表中所有男同学的sname、address字段创建一个视图NewColumnName_View,这两个字段的字段名定义为Boy_Name和Boy_Address。实例代码如下。

    CREATE VIEW  NewColumnName_View(Boy_Name, Boy_Address,sex)
    AS
    SELECT sname, address,sex
    FROM  StudentInfo
    WHERE  sex = '男'

查看视图的数据。

    SELECT *
    FROM  NewColumnName_View

运行结果如图4-16所示。

图4-16 与表具有不同字段名的视图信息

实际应用时,我们要注意新定义的字段名与表中数据的对应关系。

4.4.3 利用视图简化表的复杂连接

视图的一个重要用途就是进行复杂的SQL数据处理。通过创建视图,我们可以实现多表之间的复杂连接。将频繁使用的连接定义成视图后,用户就不必每次使用时都要指定复杂的连接条件了。

说明

本小节和4.4.4节用到了一些前面并没有介绍过的语句,如果读者没有一定的SQL基础,可先略过。在本书的后面章节中,我们会对这些语句有详细的讲解。

在4.2.2节,介绍了3个相互关联的表——学生信息表(StudentInfo)、招生信息表(RecruitInfo)和院系信息表(Department)。在实际应用中,用户一次查询的记录往往分别存储在这3个不同的表中,如查询“李山”同学所在的系以及高考时所在地区的录取分数线信息。这时,需要的3个字段sname、dname和dnum分别在这3个不同的表中。如果我们采用连接表的方式查询,每次查询不同的同学都需要通过复杂的连接条件连接一次表,显然效率低下;而如果为sname、dname和dnum这3个字段的记录创建一个视图,我们只需要进行一次表的连接,而后相关的查询只要访问视图就可以了,明显提高了查询效率。

实例17 利用视图简化表的复杂连接

创建一视图Join_View,包含StudentInfo表中的姓名(sname)字段、RecruitInfo表中的系名(dname)以及Department表中的录取分数(dnum)的信息。实例代码如下。

    CREATE VIEW  Join_View
    AS
    SELECT  sname, dname, score
    FROM   StudentInfo, Department, RecruitInfo
    WHERE  StudentInfo.address = RecruitInfo.address
    AND    StudentInfo.dno = Department.dno

查看视图的数据。

    SELECT * FROM  Join_View

运行结果如图4-17所示。

图4-17 查询视图

创建了视图Join_View后,用户以后需要对姓名(sname)、所在系名(dname)以及所在地录取分数(dnum)的信息查询,只要对视图Join_View进行操作就可以了,而不必每次都对表进行连接。

4.4.4 利用视图简化复杂查询

在多表之间进行一系列复杂查询时,利用视图可以将查询条件层层分解,简化查询过程。下面我们通过一个具体实例说明利用视图将复杂查询的层层分解过程。

实例18 利用视图简化复杂查询

从StudentInfo表、RecruitInfo表和Department表中查找录取分数高于630分的男同学的姓名及其所在的系名。

(1)基于StudentInfo表,创建视图Boys_View,包含所有男同学的信息。代码如下。

    CREATE VIEW  Boys_View
    AS
    SELECT  *
    FROM   StudentInfo
    WHERE  sex = '男'

查看视图Boys_View的数据。

    SELECT * FROM  Boys_View

运行结果如图4-18所示。

图4-18 创建基于表的视图

(2)基于RecruitInfo表,创建视图Score_View,包含录取分数高于630的所有学生信息。代码如下。

    CREATE VIEW  Score_View(sno ,sname, sex, address,dno)
    AS
    SELECT  StudentInfo.*
    FROM  StudentInfo, RecruitInfo
    WHERE  StudentInfo. address = RecruitInfo. address
    AND  RecruitInfo. Score>630

查看视图Score_View的数据。

    SELECT * FROM  Score_View

运行结果如图4-19所示。

图4-19 创建基于表的带有条件的视图

(3)基于Score_View视图和Boys_View视图,创建视图BoyScore_View,包含录取分数高于630的所有男学生信息。代码如下。

    CREATE VIEW  BoyScore_View
    AS
    SELECT *  FROM  Score_View
    WHERE sno  IN
    (SELECT sno  FROM  Boys_View)

查看视图BoyScore_View的数据。

    SELECT * FROM  BoyScore_View

运行结果如图4-20所示。

图4-20 创建基于多个视图的视图

(4)基于视图BoyScore_View和表Department,创建视图Result_View,包含录取分数高于630分的男同学的姓名及其所在的系信息。代码如下。

    CREATE VIEW  Result_View (sname, dname)
    AS
    SELECT  BoyScore_View. Sname, Department.dname
    FROM  BoyScore_View, Department
    WHERE  BoyScore_View. dno = Department. dno

查看视图Result_View的数据。

    SELECT *  FROM  Result_View

运行结果如图4-21所示。

图4-21 查看视图的数据

由此可见,通过创建视图层层分解,多表的复杂查询变得简洁、清楚。

4.4.5 视图的销毁

在SQL中,我们可以通过DROP VIEW关键字销毁创建的视图。语法如下。

    DROP  VIEW view_name

销毁视图,只需在DROP VIEW关键字后写入视图的名称即可。

实例19 销毁视图并查询销毁视图后表的信息

销毁4.4.2节为StudentInfo表创建的视图StudentInfo_View。代码如下。

    DROP  VIEW  StudentInfo_View

此时,查看视图StudentInfo_View数据。

    SELECT * FROM  StudentInfo_View

运行结果如下。

    Invalid object name 'StudentInfo_View'.

这表明该视图对象在数据库中已经不存在。

强调一点,视图在物理上是不存在的,它实际上只是一个查询结果,是一个被存储的查询。与创建表CREATE TABLE语句不同,CREATE TABLE语句在系统目录中保存表,而CREATE VIEW语句只保存视图的定义。所以DROP VIEW语句删除视图时,删除的也只是视图的定义,对实际表中的数据并没有任何影响。

此时查看StudentInfo表中的数据如图4-22所示。

图4-22 StudentInfo表中的数据

我们可以发现,虽然视图被销毁了,但是表中的数据没有任何变化。

注意

视图被销毁后,实际的表并不会发生任何改变。

4.4.6 使用视图的几点原则

一般情况下,创建和使用视图应遵循以下几点原则。

和表一样,视图必须有惟一的名字。不仅视图之间不允许有相同的名字,并且视图与表也不允许拥有相同的名字。

视图的创建个数不受限制,用户可以创建任意多个视图。

用户要创建视图,必须从数据库管理员那里得到创建权限。

视图可以嵌套,即可以创建视图的视图。

一些数据库管理系统(如SQL Server)禁止用户在查询语句中使用ORDER BY子句。