- SQL查詢的藝術
- 張權 郭天嬌
- 2661字
- 2019-01-02 08:08:13
第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子句。