第8章 索引与视图
为了提高对数据表或视图的搜索效率。
8.1.2 何种情况下创建索引
8.1.3 索引的原理――B_树
(1)树中每个节点最多有m棵子树。
(2)根节点除外,所有非叶子节点至少都包含m/2棵子树。
(3)若根节点不是叶子节点,则根节点至少两棵子树。
(4)所有非叶子节点都包含相应的关键信息,一个包含k+1棵子树的非叶节点恰好包含k个关键字。
k表示关键字的个数。Ki(i=1,2,…,k)为关键字,且Ki < Ki+1。Ai(i=0,2,…,k)为指向相应子树根节点的指针,且指针Ai-1所指子树中所有节点的关键字均小于Ki(i=1,2,…,k),而Ai+1所指子树中所有节点的关键字均大于Ki(i=1,2,…,k-1)
(5)所有的叶子节点都出现在同一层次上,并且叶子节点不包含任何关键字信息。
8.2.1 聚集索引和非聚集索引
1. 聚集索引(Clustered Index)
使用BETWEEN、>=、>、<=、<等运算符的查询
使用JOIN子句的查询
使用GROUP BY子句的查询
返回大结果集的查询
字段值唯一的字段(特别是标识字段),或绝大部分字段值都不重复的字段,如90%字段值都不重复的字段
按顺序被访问的字段
更新频繁的字段。因为在数据更新时,为保持与聚集索引的一致性必须移动表中的记录。对数据量大的数据表而言这种移动过程是耗时的,因而是不可取的。
宽度比较长的字段。因为非聚集索引键值都包含聚集索引键,这会导致所有非聚集索引的“膨胀”,增加非聚集索引的长度,降低查询效率。
2. 非聚集索引(Non-Clustered Index)
【例子】 我们考虑表student中对s_no字段创建的非聚集索引。其可能的非聚集索引结果如图8.3所示。
非聚集索引的索引指针是允许“相交”的(这是非聚集索引的主要特点),这是因为在非聚集索引中B_树的叶子结点保存的是记录的指针,索引顺序与数据记录的物理顺序不需要保持一致,只要索引指针正确指向相应的记录即可。
8.2.2唯一索引与非唯一索引
8.2.3 组合索引
8.3.1 聚集索引
CREATE CLUSTERED INDEX index_name ON table_name(col_list [DESC | ASC]);
【例8.1】 创建一个聚集索引。
CREATE TABLE student2( s_no char(8) , s_name char(8) NOT NULL, s_sex char(2) CHECK(s_sex = '男' OR s_sex = '女’), s_birthday smalldatetime CHECK(s_birthday>='1970-1-1' AND s_birthday<='2000-1-1’), s_speciality varchar(50) DEFAULT '计算机软件与理论’, s_avgrade numeric(3,1) CHECK(s_avgrade >= 0 AND s_avgrade <= 100), s_dept varchar(50) DEFAULT '计算机科学系' );
INSERT student2 VALUES('20170205','贾簿','男','1994-09-03','计算机软件与理论',43.0,'计算机系'); INSERT student2 VALUES('20170206','李思思','女','1996-05-05','计算机应用技术',67.3,'计算机系'); INSERT student2 VALUES('20170207','蒙恬','男','1995-12-02','大数据技术',78.8,'大数据技术系'); INSERT student2 VALUES('20170208','张宇','女','1997-03-08','大数据技术',59.3,'大数据技术系'); INSERT student2 VALUES('20170201','刘洋','女','1997-02-03','计算机应用技术',98.5,'计算机系'); INSERT student2 VALUES('20170202','王晓珂','女','1997-09-20','计算机软件与理论',88.1,'计算机系'); INSERT student2 VALUES('20170203','王伟志','男','1996-12-12','智能科学与技术',89.8,'智能技术系'); INSERT student2 VALUES('20170204','岳志强','男','1998-06-01','智能科学与技术',75.8,'智能技术系');
SELECT * FROM student2;
CREATE CLUSTERED INDEX myIndex1 -- 创建聚集索引myIndex1 ON student2(s_no DESC);
8.3.2 非聚集索引
CREATE [CLUSTERED] INDEX index_name ON table_name(col_list [DESC | ASC]);
【例8.2】 创建一个非聚集索引。
CREATE NONCLUSTERED INDEX myIndex2 -- 创建非聚集索引myIndex2 ON student(s_name);
上述代码中,关键字NONCLUSTERED可以省略。
【例8.3】 强制引用指定的非聚集索引。
SELECT * FROM student WITH (INDEX (myIndex2)) -- 强制引用索引myIndex2 WHERE s_name='刘洋';
8.3.3 唯一索引
CREATE UNIQUE INDEX index_name ON table_name(col_list [DESC | ASC]);
【例8.4】 创建一个唯一非聚集索引。
在本例中,对表student的s_avgrade列创建一个唯一非聚集索引,使索引列降序排序。相应代码如下:
CREATE UNIQUE INDEX myIndex3 -- 创建唯一非聚集索引myIndex3 ON student(s_avgrade DESC)
CREATE UNIQUE INDEX myIndex5 ON SC(s_no DESC, c_name ASC);
8.4.1 查看索引
sp_helpindex [ @objname = ] 'name‘;
index_name:返回索引名;
index_description:返回索引说明,如是否是聚集索引、唯一索引等信息,其中包括索引所在的文件组;
index_keys:返回对其生成索引的列。
sp_helpindex 'student';
USE MyDatabase; GO SELECT * FROM sys.indexes;
8.4.2 删除索引
DROP INDEX index_name ON table_name;
DROP INDEX table_namet.index_name;
DROP INDEX myIndex2 ON student; --也可以写成: DROP INDEX student.myIndex2
【例8.9】 删除定义PRIMARY KEY约束时创建的索引。
ALTER TABLE student DROP CONSTRAINT PK__student__2F36BC5B91406173
8.5.1 视图的概念
8.5.2 视图的优缺点
(3)自组织数据
(4)组合分区数据
(5)便于数据共享
(6)提高安全性
(1)相对低效
(2)有限的更新操作
8.6.1 创建视图
CREATE VIEW view_name [(column [,...n])] AS select_statement;
【例8.10】 创建与基表完全相同的视图。
CREATE VIEW myView1 AS SELECT * FROM student;
SELECT * FROM myView1
可以看到,视图myView1中的数据与表student中的数据是完全一样的
【例8.11】 创建包含基表中部分数据的视图,并给视图设定新的中文字段名。
CREATE VIEW myView2(学号,姓名,专业,平均成绩,系别) AS SELECT s_no, s_name, s_speciality, s_avgrade, s_dept FROM student WHERE s_avgrade>=60;
SELECT * FROM myView2
--注意,视图myView2包含的字段名为“学号”、“姓名”、“专业”、“平均成绩”和“系别”, --而s_no, s_name, s_speciality, s_avgrade, s_dept不是该视图的字段名了, --因此下列的SELECT语句都是错误的: SELECT s_no, s_name, s_speciality, s_avgrade, s_dept FROM myView2 SELECT s_no, s_name FROM myView2 --而下列的SELECT则是正确的: SELECT 学号,姓名,专业,平均成绩,系别 FROM myView2
【例8.12】 创建带有两个基表的视图。
CREATE VIEW myView3 AS SELECT student.s_no 学号, s_name 姓名, s_sex 性别, s_speciality 专业, s_dept 系别, c_name 课程名称, c_grade 课程成绩 FROM student, SC WHERE student.s_no = SC.s_no
【例8.13】 用视图更新数据表。
CREATE VIEW tmp_view AS SELECT s_no, AVG(c_grade) as s_avgrade FROM SC GROUP BY s_no
其次,表student和视图tmp_view的联系是基于字段s_no的(1:1)联系,因此我们可以用视图tmp_view中的平均成绩字段s_avgrade去更新表student中的平均成绩字段s_avgrade:
UPDATE student SET s_avgrade = b.s_avgrade FROM student as a JOIN tmp_view as b ON a.s_no = b.s_no
CREATE FUNCTION myFunView(@a float, @b float) RETURNS TABLE AS RETURN ( SELECT * FROM student WHERE s_avgrade >= @a and s_avgrade <= @b );
SELECT * FROM myFunView(60,90)
UPDATE myFunView(60,90) SET s_dept= '智能技术系‘
8.6.2 更新视图
UPDATE myView2 SET 平均成绩 = 80;
UPDATE myView3 SET 性别 = '女', 课程成绩 = 99
UPDATE myView3 SET 性别 = '女’ UPDATE myView3 SET 课程成绩 = 99
8.6.3 删除视图
DROP VIEW [ schema_name . ] view_name [ ...,n ] [ ; ]
DROP VIEW myView1, myView2;
8.7.1 视图的定义代码
如果视图的定义代码没有被加密,我们可以查看它的定义代码。
查看视图myView1的定义代码,可以利用系统存储过程sp_helptext来实现。其实现代码如下:
sp_helptext myView1;
执行上述存储过程,结果如图8.10所示。
8.7.2 视图的结构信息
本例将查看视图myView1,使用系统存储过程sp_help的代码如下:
sp_help myView1;
执行上述代码,结果如图8.11所示。
8.7.3 数据库中的视图
【例8.19】 查看当前数据库中所有用户定义的视图。
USE MyDatabase; GO SELECT name '视图名称(当前数据库)' FROM sys.views; GO
执行上述代码,结果如图8.12所示。
SELECT name '视图名称(所有数据库)' FROM sys.all_views;