第六章 - 索引和视图
第六章 - 索引和视图
6.1 索引
在MySQL数据库中,索引(Index)是影响数据性能的重要因素之一,设计高效的、合理的索引可以显著提高数据信息的查询速度和应用程序的性能。
视图(View)是一个存储指定查询语句的虚拟表,视图中数据来源于由定义视图所引用的表,并且能够实现动态引用,即表中数据发生变化,视图中的数据随之变化。
本章将介绍索引和视图等数据库对象的基本概念和常用操作。
索引由数据库表中一列或多列组合而成的一种特殊的数据库结构,利用索引是可以用来快速查询数据库表中的特定记录信息。在MySQL中,所有的数据类型都可以被索引。
6.1.1 索引概念
MySQL的索引是为了加速对数据进行检索而创建的一种分散的、物理的数据结构。索引包含从表或视图中一个或多个列生成的键,以及映射到指定数据行的存储位置指针。
索引是依赖于表建立的,提供了数据库中编排表中数据的内部方法。表的存储由两部分组成,一部分是表的数据页面,另一部分是索引页面。索引就存放在索引页面上。
数据库中的索引的形式与图书的目录相似,键值就像目录中的标题,指针相当于页码。索引的功能就像图书目录能为读者提供快速查找图书页面内容一样,不必扫描整个数据表而找到想要的数据行。
当MySQL数据库在执行一条查询语句的时候,默认的执行过程是根据搜索条件进行全表扫描,遇到匹配条件的就加入搜索结果集合。如果查询语句涉及多个表连接,包括了许多搜索条件(例如大小比较、like匹配等),而且表数据量特别大的时,在没有索引的情况下,MySQL需要执行的扫描行数会很大,速度也会很慢。
索引一旦创建,将由数据库自动管理和维护。例如,向表中插入、更新和删除一条记录时,数据库会自动在索引中做出相应的修改。在编写SQL查询语句时,具有索引的表与不具有索引的表没有任何区别,索引只是提供一种快速访问指定记录的方法。实际过程中,当MySQL执行查询时,查询优化器会对可用的多种数据检索方法的成本进行估计,从中选用最有效的查询计划。
在数据库中使用索引的优点如下。
(1)加速数据检索:索引能够以一列或多列值为基础实现快速查找数据行。
(2)优化查询:查询优化器是依赖于索引起作用的,索引能够加速连接、排序和分组等操作。
(3)强制实施行的唯一性:通过给列创建唯一索引,可以保证表中的数据不重复。
需要注意的是,索引并不是越多越好,要正确认识索引的重要性和设计原则,创建合适的索引。
6.1.2 索引分类
按照分类标准的不同,MySQL的索引有多种分类形式。 MySQL的索引通常包括普通索引(index)、唯一性索引(unique)、主键(primary key)、全文索引(fulltext)和空间索引(spatial)等类型。
1. 普通索引(index)
索引的关键字是index。普通索引是MySQL中的基本索引类型,允许在定义索引的列中插入重复值和空值。
2. 主键索引(primary key)
是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引。也可通过修改表的方法增加主键,但一个表只能有一个主键索引。
3. 唯一性索引(unique)
unique索引列的值必须唯一,允许有空值。如果是组合索引,则列值的组合必须唯一。在一个表上可以创建多个unique索引。
4. 全文索引(fulltext)
全文索引是指在定义索引的列上支持值的全文查找,允许在这些索引列中插入重复值和空值。该索引只能对char、varchar和text类型的列编制索引,并且只能在MyISAM表中编制。即MySQL中只有MyISAM存储引擎支持全文索引。在MySQL默认情况下,对于中文作用不大。
5. 空间索引(spatial)
空间索引是对空间数据类型的字段建立的索引。MySQL中的空间数据类型有4种,分别是geometry、point、linestring和polygon。MySQL使用spatil关键字进行扩展,使得能够用于创建正规索引类似的语法创建空间索引。创建空间索引的列,必须将其声明为not null,空间索引只有在存储引擎MyISAM的表中创建。对于初学者来说,这类索引很少会用到。
如果按照创建索引键值的列数分类,索引还可以分为单列索引和复合索引。
如果按照存储方式分类,MySQL的索引分为B-Tree索引和Hash索引。
MySQL的MEMORY数据引擎还支持Hash索引。Hash索引相对于B-Tree索引,检索效率要高上不少。
但MySQL Hash索引本身的特殊性也带来了很多限制和弊端,主要有以下内容。
(1) 仅仅能满足“=” ,“in”和“<=>”查询,不能使用范围查询。
(2) 无法被用来避免数据的排序操作。
(3) 无法被用来避免数据的排序操作。
(4) 不能利用部分索引键查询。
(5) 在任何时候都不能避免表扫描。
(6) 遇到大量Hash值相等的情况后,性能并不一定就会比B-Tree索引高。
说明:
(1) MySQL中运算符“ <=>”除了能够像常规的“=”运算符一样,对两个值进行比较外,还能够用于比较null值。
(2) 运算符“ <=>”和“=”号的相同点。像常规的“=”运算符一样,两个值进行比较,结果是0(不相等)或1(相等)。例如,'A'<=>'B'的值为0,'a'<=>'a'的值为1。
(3) 运算符“ <=>”和“=”号的不同点。和“=”运算符不同的是,null与常量进行比较运算,其值直接处理为null。使用“ <=>”运算符时,例如,'a' <=> null 的值0 null<=> null 的值为 1;相当于 'a' is null和null is null。而'a' is not null 则相当于 not('a' <=> null)。
6.1.3 设置索引的原则
在数据表中创建索引,为使索引的使用效率更高,必须考虑在哪些字段上创建索引和创建什么类型的索引。首先要了解以下常用的基本原则。
(1) 一个表创建大量索引,会影响insert、update和delete语句的性能。应避免对经常更新的表创建过多的索引,要限制索引的数目。
(2) 若表的数据量大,对表数据的更新较少而查询较多,可以创建多个索引来提高性能。在包含大量重复值的列上创建索引,查询的时间会较长。
(3) 经常需要排序、分组和联合操作的字段一定要建立索引,即将用于join、 where判断和order by排序的字段上创建索引。
(4)在视图上创建索引可以显著的提升查询性能。
(5)尽量不要对数据库中某个含有大量重复的值的字段建立索引,在这样的字段上建立索引有可能降低数据库的性能。
(6)在主键上创建索引,在InnoDB中如果通过主键来访问数据效率是非常高的。每个表只能创建一个主键索引。
(7) 要限制索引的数目,对于不再使用或者很少使用的索引要及时删除。
(8)InnoDB数据引擎的索引键最长支持是767字节,MYISAM数据引擎支持1000字节。
6.1.4 创建索引
创建索引通常有3种命令方式,即创建表时附带创建索引、通过修改表来创建索引和使用alter table
语句来创建索引。
1. 利用create index
语句创建3种索引
如果基表已经创建完毕,就可以使用create index
语句建立索引。
创建索引基本形式如下:
create [unique|fulltext|spatial] index index_name
on table_name (index_col_name,...)
创建索引时,可以使用col_name(length)语法对前缀编制索引。前缀包括每列值的前length个字符。对于char和varchar列,只用1列的一部分就可创建索引。blob和text列也可以编制索引,但是必须给出前缀长度。
因为多数名称的前10个字符通常不同,所以前缀索引不会比使用列的全名创建的索引速度慢很多。另外,使用列的一部分创建索引可以使索引文件大大减小,从而节省了大量的磁盘空间,有可能提高insert操作的速度。
【例6.1】为便于按电话进行查询,为student表的phone列上建立一个升序普通索引phone_index。
mysql> use teaching;
mysql> create index phone_index on student(phone asc);
【例6.2】在course表的cname列上建立一个唯一性索引cname_index。
mysql> create unique index cname_index on course (cname);
【例6.3】在score表的studentno和courseno列上建立一个复合索引sc_index。
mysql> create index sc_index on score(studentno,courseno);
2. 创建表时创建索引
创建表时可以直接创建索引,这种方式最简单、方便。
【例6.4】创建teacher1表的tname字段建立一个唯一性索引tname_index,一个前缀索引dep_index。
mysql> use mysqltest;
mysql> create table if not exists teacher1 (
-> teacherno char(6) not null comment '教师编号',
-> tname char(8) not null comment'教师姓名',
-> major char(10) not null comment '专业',
-> prof char(10) not null comment '职称',
-> department char(16) not null comment '部门',
-> primary key (teacherno),
-> unique index tname_index(tname),
-> index dep_index(department(5))
-> );
3. 通过alter table
语句创建索引
【例6.5】在teacher1表上建立teacherno主键索引(假定未创建主键索引),建立tname和prof的复合索引。
mysql> alter table teacher1
-> add primary key(teacherno),
-> add index mark(tname, prof);
如果主键索引已经创建,则会出现如下信息:
ERROR 1068 (42000): Multiple primary key defined
说明:
(1) 只有表的所有者才能给表创建索引。索引的名称必须符合MySQL的命名规则,且必须是表中唯一的。
(2) 主键索引必定是唯一的,唯一性索引不一定是主键。一张表上只能一个主键,但可以有一个或者多个唯一性索引。
(3) 当给表创建unique约束时,MySQL会自动创建唯一索引。创建唯一索引时,应保证创建索引的列不包括重复的数据,并且没有两个或两个以上的空值(null)。因为创建索引时将两个空值也视为重复的数据,如果有这种数据,必须先将其删除,否则索引不能被成功创建。
(4) 若要查看表中已经创建索引的情况,可以使用show index from table_name语句实现。
6.1.5 删除索引
删除不再需要的索引,可以通过drop语句来删除索引,也可用alter table
语句删除。
1. drop index
利用drop index
语句删除索引的语法格式如下:
drop index index_name on table_name ;
例如,删除teacher1表的mark索引。
mysql> drop index mark on teacher1;
2. alter table
利用alter table语句删除索引的语法格式如下:
alter [ignore] table table_name
| drop primary key
| drop index index_name
| drop foreign key fk_symbol
利用alter table语句同样可以删除前面表中创建的索引。例如:
mysql> alter table course drop index cname_index;
6.2 视图的创建和管理
6.2.1 视图
视图是从一个或者多个表及其他视图中通过select
语句导出的虚拟表,数据库中只存放了视图的定义,而并没有存放视图中的数据。
浏览视图时所对应数据的行和列数据来自定义视图查询所引用的表,并且在引用视图时动态生成。通过视图可以实现对基表数据的查询与修改。
视图为数据库用户提供了很多的便利,主要包括以下几个方面:
(1) 简化数据查询和处理
视图可以为用户集中多个表中的数据,简化用户对数据的查询和处理。
(2) 屏蔽数据库的复杂性
数据库表的更改不影响用户对数据库的使用,用户也不必了解复杂的数据库中的表结构。例如,那些定义了若干张表连接的视图,就将表与表之间的连接操作对用户隐蔽起来了。
(3) 安全性
如果想要使用户只能查询或修改用户有权限访问的数据,也可以只授予用户访问视图的权限,而不授予访问表的权限,这样就提高了数据库的安全性。
6.2.2 创建视图
创建视图是指在指定的数据库表上建立视图。视图可以建立在一张表上,也可以建立在多张表或既有视图上。
要求创建用户具有针对视图的create view
权限,以及针对由select
语句选择的每一列上的某些权限。创建视图的语法形式 。
语法形式如下:
create [or replace][algorithm ={ undefined|merge|temptable }]
view view_name [(column_list)]
as select_statement
[ with[ cascaded|local] check option ] ;
创建视图有如下限制:
(1) 在视图的from子句中不能使用子查询。
(2) 在视图的select语句不能引用系统或用户变量。
(3) 在视图的select语句不能引用预处理语句参数。
(4) 在视图定义中允许使用order by,但是,如果从特定视图进行了选择,而该视图使用了具有自己order by的语句,它将被忽略。
(5)在定义中引用的表或视图必须存在。但是,创建了视图后,能够舍弃定义引用的表或视图。要想检查视图定义是否存在这类问题,可使用check table语句。
(6)在定义中不能引用temporary表,不能创建temporary视图。
(7)不能将触发程序与视图关联在一起。
1. 在单表上创建视图
MySQL中可以在单个表上创建视图。
【例6.6】在teacher表上创建一个简单的视图,视图名称为teach_view1。
mysql> create view teach_view1
-> as select * from teacher;
可以利用select语句查询视图teach_view1的数据如下。
mysql> select * from teach_view1;
2. 在多表上创建视图
MySQL数据库中也可以在两个或两个以上的表上创建视图。
【例6.7】在student表、course表和score表上创建一个名为stu_score1的视图。视图中保留18级的女生的学号、姓名、电话、课程名和期末成绩。
mysql> create view stu_score1
-> as select student.studentno, sname, phone, cname,final
-> from score join student on student.studentno=score. studentno
-> join course on course.courseno=score.courseno
-> where sex=‘女’ and left(student.studentno,2)= ‘18’;
此视图保存在3个表的数据,可以利用select语句查询视图stu_score1的数据如下。
mysql> select * from stu_score1;
3. 在已存在的视图上创建视图
【例6.8】创建视图teach_view2,统计计算机学院的教师中的教授和副教授的教师号、教师名和专业。
mysql> create view teach_view2
-> as select teacherno, tname, major
-> from teach_view1
-> where prof like ‘%教授’ and department=‘计算机学院’;
说明:
(1) 定义视图时基本表可以是当前数据库的表,也可以来自于另外一数据库的基本表,只要在表名前添加数据库名称即可,如mysql.student02。
(2) 定义视图时可在视图名后面指明视图列的名称,名称之间用逗号分隔,但列数要与select语句检索的列数相等。例如,定义视图teach_view2可以写成如下方式:
create view teach_view2(教师号,教师名,专业)
as select teacherno, tname, major ……
(3) 使用视图查询时,若其基本表中添加了新字段,则该视图将不包含新字段。
(4)如果与视图相关联的表或视图被删除,则该视图将不能再使用。
6.2.3 查看视图的定义
查看视图是指查看数据库中已存在的视图的定义。查看视图必须要有show view
的权限,MySQL数据库下的user表中保存着这个信息。
查看视图的方法包括describe
语句、show table status
语句、show create view
语句和查询information_schema
数据库下的views表等。
1. describe
语句查看视图基本信息
可以使用describe
语句可以查看表的基本定义。
同样可以使用describe
语句可以用来查看视图的基本定义。
describe
语句查看视图的基本形式与查看表的形式是一样的。
mysql> describe stu_score1;
2. 利用show table status语句查看视图基本信息
MySQL中,可以使用show table status
语句来查看视图的信息。
其语法形式如下:
show table status like ‘view_name’;
mysql> show table status like 'teach_view1';
3. 利用show create view语句查看视图详细信息
MySQL中,show create view
语句可以查看视图的详细定义。
语法形式如下:
show create view view_name
mysql> show create view teach_view2;
4. 在views表中查看视图详细信息
MySQL数据库中,所有视图的定义都存在information_schema
数据库下的views表中。
例如,查询information_schema.views 表,可以查看到数据库中所有视图的详细信息。
代码如下:
select * from information_schema.views;
-- *表示查询所有的列的信息;
-- information_schema.views表示information_schema数据库下面的views表。
6.2.4 修改视图
修改视图是指修改数据库中已存在的表的定义。当基本表的某些字段发生改变时,可以通过修改视图来保持视图和基本表之间一致。
MySQL中通过create or replace view
语句和alter
语句来修改视图。
1. create or replace view
语句修改视图
MySQL中,create or replace view
语句可以用来修改视图。该语句的使用非常灵活。
在视图已经存在的情况下,对视图进行修改;视图不存在时,可以创建视图。
create or replace view
语句的语法形式如下:
CREATE
[OR REPLACE]
[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
[DEFINER = user]
[SQL SECURITY { DEFINER | INVOKER }]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]
mysql> CREATE VIEW view_create_replace
-> (id, category)
-> AS
-> SELECT id, t_category FROM t_goods_category;
Query OK, 0 rows affected (0.01 sec)
在使用CREATE OR REPLACE VIEW语句修改MySQL视图时,需要注意以下几点:
(1) 视图只能修改其实际查询的SELECT语句中的内容,而不能修改其FROM、WHERE、GROUP BY和HAVING子句。
(2) 视图一旦被修改,则其与其它视图和关联表之间的关系可能会受到影响,因此需要谨慎操作。
(3) 如果在修改视图时需要引用另一个视图或存储过程,建议在执行前将该视图或存储过程替换成其实际的查询语句或代码,以避免可能出现的错误和不可预测的行为。
2. alter
语句可修改视图
在MySQL中,alter
语句可以修改表的定义,可以创建索引。不仅如此,alter
语句还可以用来修改视图。
alter
语句修改视图的语法格式如下:
alter [algorithm = {undefined|merge|temptable}]
view view_name [(column_list)]
as select语句
[with [cascaded|local]check option];
【例6.9】修改视图teach_view2,统计计算机学院和材料学院的教师中的教授和副教授的教师号、教师名、和专业,并在视图名后面指明视图列名称。
mysql> alter view teach_view2(教师号,教师名,专业)
-> as select teacherno, tname, major
-> from teach_view1
-> where prof like '%教授'
-> and (department=‘计算机学院’ or department=‘材料学院’ );
可以通过用select语句查看视图teach_view2的数据如下。
mysql> select * from teach_view2;
6.2.5 删除视图
删除视图是指删除数据库中已存在的视图。删除视图时,只能删除视图的定义,不会删除数据。
MySQL数据库中,用户必须拥有drop
权限才能使用drop view
语句来删除视图。
对需要删除的视图,使用drop view
语句进行删除。drop view
命令可以删除多个视图,各视图名之间用逗号分隔。
基本格式如下:
drop view [if exists]视图名列表 [restrict|cascaded]
例如,删除视图V1_student命令如下:
drop view V1_student;
6.3 视图的应用
视图的使用主要包括视图的检索,以及通过视图对基表进行插入、修改、删除操作。视图的检索几乎没有什么限制,但是对通过视图实现表的插入、修改、删除操作则有一定的限制条件。
6.3.1 使用视图管理表数据
1. 使用视图进行查询
使用视图进行查询实际上就是把视图作为数据源,实现查询功能。
【例6.10】通过视图stu_view2,查询选修课程号为c08123、且成绩在80分以上的18级男生学生的学号、课程号和成绩。
mysql> select 学号, 姓名,课程号,成绩
-> from stu_view2
-> where 课程号='c08123' and 成绩 > 80;
2. 使用视图进行统计计算
【例6.11】创建视图course_avg,统计各门课程平均成绩,并按课程名称降序排列。
mysql> create view course_avg
-> as select cname 课程名, avg(final) 平均成绩
-> from score join course on score.courseno= course.courseno
-> group by cname desc;
mysql> select * from course_avg;
3. 使用视图修改基本表数据
使用视图修改表数据,是指在视图中进行insert、update和delete等操作而修改基表的数据。通过视图修改表数据时,要有执行相关操作的权限。
【例6.12】通过视图teach_view1,对基表teacher进行插入、更新和删除数据的操作。
mysql> insert into teach_view1(teacherno,tname,major,prof,department)
-> values (‘t06027’ , ‘陶期年’ , ‘纳米技术’ , ‘教授’ , ‘材料学院’ );
mysql> update teach_view1 set prof = ‘副教授’ where teacherno = ‘t07019’;
mysql> delete from teach_view1 where teacherno = ‘t08017’;
使用select语句查询teacher表,可以看到基表中的数据也相应地进行了修改。
mysql> select * from teacher ;
【例6.13】视图stu_score1依赖于源表student、course和score等3张表,包括studentno、sname、 phone、cname和final等5个字段,通过stu_score1修改基本表student中的学号为18125121107的电话号码。
mysql> update stu_score1 set phone='132123456777'
-> where studentno =‘18125121107’;
通过查看student 表,可以看到相应成绩已做了更改。
mysql> select studentno,sname, phone from student
-> where studentno ='18125121107' ;
说明:
(1) 视图若只依赖于一个基表,则可以直接通过更新视图来更新基本表的数据。
(2)若一个视图依赖于多张基表,则一次只能修改一个基表的数据,不能同时修改多个基本表的数据。
(3) 如果视图包含下述结构中的任何一种,都是不可修改的:
- 视图的列含有聚合函数(avg、count、sum、min、max)。
- 视图的列是通过表达式并使用列计算出其他列。
- 含有distinct关键字。
- 含有group by子句、order by子句、having子句。
- 含有union运算符。
- 视图的列位于选择列表中的子查询。
- from子句中包含多个表。
- select语句中引用了不可更新视图。
- where子句中的子查询,引用from子句中的表。
6.3.2 检查视图的应用
在MySQL数据库中,视图可分为普通视图与检查视图。
前面介绍的视图都没有使用with check option
子句,当没有with_check_option
时,表示with_check_option
的值为0。即为普通视图,普通视图不具备检查功能。
如果使用了with check option
子句,在通过检查视图更新基表数据时,只有满足检查条件的更新语句才能成功执行。
【例6.14】编程在teaching数据库中创建一个名称为V_ dept的视图,包含所有部门为“计算机学院”的老师的数据信息,需限制插入数据中部门必须为“计算机学院”。
分析:该程序通过单表生成的视图V_dept向基表teacher中插入一条记录,并通过查询语句显示基表中的所有数据。
--在“查询编辑器”中输入以下程序,创建V_dept视图。
mysql> create view V_dept
-> AS
-> select teacherno,tname,major,prof, department
-> from teacher
-> where department ='计算机学院'
-> with check option;
--通过视图V_ dept 向基表teacher中插入数据。
mysql> insert into V_dept
-> values(‘t08017’,‘时观’,‘金融管理’,‘副教授’,‘计算机学院’);
mysql> select * from teacher where tname='时观';
本例由于创建了with check option
检查条件约束,当插入记录时所有“部门”信息不符合条件的记录无法插入和修改,并显示错误提示信息。
--通过视图V_sex向基表teacher中插入数据行('t08037','时刻','软件技术','讲师','软件学院')。
mysql> insert into V_dept
-> values('t08037','时刻','软件技术','讲师','软件学院');
ERROR 1369 (HY000): check option failed 'teaching.v_dept'
执行结果表明,通过检查更新表数据时,检查视图对更新数据进行了先行检查,若更新语句的数据不满足检查条件,则检查视图就会抛出异常,更新失败。