数据库概述
第1章 数据库概述
数据管理技术经过多年的发展,已经发展到数据库系统阶段,在该阶段会把数据存储到数据库(DataBase,DB)中,即数据库相当于存储数据的仓库,为了便于用户组织和管理数据,还专门提供了数据库管理系统(DataBase Mananagement System,DBMS),可以有效的管理存储在数据库中的数据.本书所讲的MySQL软件,就是一种非常优秀的数据库管理系统.
通过本章的学习,可以掌握如下的内容:
l 数据管理技术
l 数据库相关概念和知识
l MySQL数据库基础概念和知识
1.1. 关于****数据库的基本概念
在目前阶段,如果要存储和管理数据离不开数据库,当数据存储到数据库后,就会通过数据库管理系统对这些数据进行组织和管理.本章将详细介绍嘻嘻数据库的基本概念.
1.1.1 数据****库管理技术的发展阶段
所谓数据管理,是指对各种数据进行分类、组织、编码、存储、检索和维护。发展到现在,数据管理技术经历了三个阶段,分别为人工管理阶段、文件系统阶段和数据库系统阶段。
\1. 人工管理****阶段
20世纪50年代中期以前,由于计算机中的硬件还没有像现在这样的磁盘,软件没有专门管理数据的软件,所以计算机值局限于科学技术方面,数据则由计算和处理的程序自动携带。该时期被称为人工管理阶段。
人工管理阶段的特点如下:
l 数据不能长期保存。
l 程序本身管理数据。
l 数据不能共享。
l 数据不具有独立性。
\2. 文件****系统阶段
随着技术的发展,在20世纪50年代后期到20世纪60年代中期,计算机不仅应用于科学技术,而且开始用于管理。在该时期由于计算机硬件出现了磁盘,计算机软件出现了高级语言和操作系统。因此程序和数据有一定的独立性。出现了城西文件和数据文件,这就是所谓的文件系统阶段。
文件系统阶段的特点如下:
l 数据可以长期保存。
l 数据由文件系统来管理。
l 数据冗余大,共享性差。
l 数据独立性差。
\3. 数据库系统阶段
随着网络技术的发展,计算机软/硬件功能的进步,在20世纪60年代后期,计算机应用于管理系统,而且规模越来越大,应用越来越广泛,数量急剧增长,对共享功能的要求越来越强烈,这样还使用文件系统管理数据,则远远不能满足当时各种应用需求。于是出现了数据库技术来统一管理数据,特别是关系型数据库技术。该阶段就是多为的数据库系统阶段。
数据库系统的出现,满足了多用户,多应用共享数据的需求,比文件系统具有明显的优点,标志着数据管理技术的飞跃。
数据库系统的特点如下:
l 数据实现结构化。
l 数据实现共享性。
l 数据独立性强。
l 数据粒度变小。
1.1.2 数据库系统阶段涉及的概念
到目前为止,处理数据的技术仍然处于数据库系统阶段,在该阶段处理数据时,经常会涉及到各种概念:数据库,数据库管理系统和数据库系统。同时如果想完全掌握数据库系统的数据处理技术,就必须熟悉和掌握这些概念。
数据库(DataBase ,DB)是指长期保存在计算机的存储设备上,按照一定的规则组织起来,可以被各种用户或应用共享的数据集合。
数据库管理系统(DataBase Management System ,DBMS )是指一种操作和管理数据库的大型软件,用于建立,使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中的数据。当前比较流行和常用的数据库管理系统有Oracle、MySQL,SQL Server 和DB2等。
数据库系统(DataBase System,DBS)是采用数据库技术的计算机系统,是由数据库(数据)、数据库管理系统、数据库管理员(人员)、支持数据库系统的硬件或软件(应用开发工具、应用系统等)以及用户5部分构成的运行实体,如图1.1所示。其中数据库管理员(DataBase Administrator, DA)是对数据库进行规划、设计、维护和监视等的专业管理人员,在数据库系统中起着非常重要的作用。

通常情况下,经常会用数据库来表示他们使用的数据库软件,这经常会引起混淆,确切地说数据库软件应该为数据库管理系统,数据库是通过数据库管理系统创建和操作的容器。
1.2. 数据模型
1.2.1. 数据模型的概念
数据模型是数据库系统的核心与基础,是关于描述数据与数据之间的联系、数据的语义、数据一致性约束的概念性工具的集合。
数据模型通常是由数据结构、数据操作和完整性约束3部分组成,分别如下。
(1) 数据结构:是对系统静态特征的描述,描述对象包括数据的类型、内容、性质和数据之间的相互关系。
(2) 数据操作:是对形同动态特征的描述,是对数据库各种对象实例的操作。
(3) 完整性约束:是完整性规则的集合,它定义了给定数据模型中数据及其联系所具有的制约和依存规则。
1.2.2. 常见的****数据模型
常用的数据库数据模型主要有层次模型、网状模型和关系模型,下面进行介绍。
(1) 层次模型:用树状结构表示试题类型及实体间联系的数据模型称为层次模型,如图1.2所示。它具有以下特点。
① 每棵数有且仅有一个无双亲父节点,称为根。
② 树中除根外所有节点有且仅有一个双亲。
(2) 网状模型:用有向图结构表示实体类型及实体间联系的数据模型称为网状模型,如图1.3所示。用网状模型编写应用程序及其复杂,数据的独立性较差。
网状结构的特点:
① 允许有一个以上的节点无双亲。
② 至少有一个节点可以有多于一个的双亲

(3) 关系模型:以二维表来描述数据。关系模型中,每个表有多个自断裂和记录行,每个字段列有固定的属性(数字、字符、日期等),如图1.4所示。关系模型数据结构简单、清晰、具有很高的数据独立性,是目前主流的数据库数据模型。
关系模型的基本属于如下。
① 关系:一个二维表就是一个关系。
② 记录:二维表中的一行,即表中的记录
③ 字段:二维表中的一列,用类型和值表示。
④ 域:每个属性取值的变化范围,如性别的域为{男,女}。
关系中的数据约束如下。
① 实体完整性约束:约束关系的主键中属性值不能为空值。
② 参照完整性约束:关系之间的基本约束。
③ 用户定义的完整性约束:反映了具体应用中数据的语义要求。

1.2.3. 关系数据库的规范化
关系数据库的规范化理论为:关系数据库中的每一个关系都要满足一定的规范。根据满足规范的条件不同,可以分为5个等级:第一范式(1NF)、第二范式(2NF)、......、第五范式(5NF)。其中,NF是Normal Form的缩写。一般情况下,只要把数据规范到第三范式的标准就可以满足需要了。下面介绍前3种范式。
\1. 第一范式(1NF)
在一个关系中,消除重复的字段,且各字段都是最小的逻辑存储单位。第一范式是第二和第三范式的基础,是最基本的范式。第一范式包括下列指导原则。
(1) 数据组的每个属性只可以包含一个值。
(2) 关系中的每个数组必须包含相同数量的值
(3) 关系中的每个数组一定不能相同。
在任何一个关系数据库中,第一范式是对关系模型的基本要求,不满足第一范式的数据库就不是关系数据库。
如果数据表中的每个列都是不可再分割的基本数据项----即同一列中不能有多个值,那么就称此数据表符合第一范式,由此可见第一范式具有不可再分解的原子特性。
在第一范式中,数据表的每一个行只包含一个实体信息,并且每一行的每一列只能存放实体的一个属性。例如,对于学生信息,不可以将学生实体的所有属性信息(如学号、姓名、性别、年龄、班级等)都放在一个列中显示,也不能将学生实体的两个或多个属性信息放在一个列中显示。
如果数据表中的列信息都符合第一范式,那么在数据表中的字段都是单一的、不可再分的。如表1.1 就是不符合第一范式的学生信息表,因为“班级”列中包含“系别”和“班级”两个属性信息,这样“班级”列中的信息就不是单一的,是可以再分的;而表1.2就是符合第一范式的学生信息表,它将原“班级”列的信息拆分到“系别”列和“班级”列中 。
表1.1 不符合第一范式的学生信息表
学号 | 姓名 | 性别 | 年龄 | 班级 |
---|---|---|---|---|
9527 | 东方 | 男 | 20 | 计算机系3班 |
表1.2 符合第一范式的学生信息表
学号 | 姓名 | 性别 | 年龄 | 系别 | 班级 |
---|---|---|---|---|---|
9527 | 东方 | 男 | 20 | 计算机系 | 3班 |
\2. 第二范式(2NF)
第二范式是在第一范式的基础上建立起来的,即满足第二范式必先满足的第一范式。第二范式要求数据表中的每个实体(即每个记录行)必须可以被唯一的区分。为实现区别各行记录通常需要为表设置一个“区分列”,用以存储各个实体的唯一标识。在学生信息表中设置“学号”列,由于每个学生的编号都是唯一的,因此每个学生可以被唯一地区分(既是学生存在重名的情况下),那么这个唯一的属性列被称为主关键字或主键。
第二范式要求实体的属性完全依赖于主关键字,即不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这个部分应该分离出来形成一个新的实体。新实体与原实体之间是一对多的关系。
如,这里以“员工工资信息表”为例,若以(员工编号、岗位)为组合关键字(即复合主键),就会存在如下决定关系。
(员工编号,岗位)-->(决定)(姓名、年龄、学历、基本工资、绩效工资、奖金)
在上面的决定关系中,还可以进一步拆分为如下的两种决定关系。
(员工编号)-->(决定) (姓名、年龄、学历)
(岗位)-->(决定) (基本工资)
其中,员工编号决定了员工的基本信息(姓名、年龄、学历);而岗位决定了基本工资,所以这个关系表不满足第二范式。
对于上面的这种关系,可以把上述两个关系更改为如下3个表。
员工档案表:EMPLOYEE (员工编号、姓名、年龄、学历)
岗位工资表:QUARTERS(岗位和基本工资)
员工工资表:PAY(员工编号、岗位、绩效工资和奖金)
\3. 第三范式(3NF)
第三范式是在第二范式的基础上建立来的,即满足第三范式必先满足第二范式。第三范式要求关系表不存在非关键字列对任意候选关键字列的传递函数依赖,也就是说,第三范式要求一个关系表中不包含已在其他表中包含的非关键字信息。
所谓传递函数依赖,就是指如果存在关键字段A决定非关键字段B,而非关键字段B决定非关键字段C,则称非关键字段C传递函数依赖于关键字段A。
例如,这里以员工信息表(EMPLOYEE)为例,该表中包含员工编号、员工姓名、年龄、部门编号、部门经理等信息,该关系表的关键字“员工编号”,因此存在如下的决定关系。
(员工编号)-->(决定) (员工姓名、年龄、部门编号、部门经理)
上面的这个关系表是符号第二范式的,但它不符合第三范式,因此该关系表内部隐含着如下决定关系。
(员工编号)-->(决定) (部门编号)-->(决定)(部门经理)
上面的关系表存在非关键字段“部门经理”对关键字段“员工编号”的传递函数依赖。对于上面的这种关系,可以把这个关系表(ENPLOYEE)更改为如下两个关系表。
员工信息表:EMPLOYEE(员工编号、员工姓名、年龄和部门编号)
部门信息表:DEPARTMENT(部门编号和部门经理)
对于关系型数据库的设计,理想的设计目标是按照“规范化”原则存储数据,因为这样做能够消除数据冗余、更新异常、插入异常和删除异常。
1.2.4. 关系数据库的设计原则
数据库设计是指对于一个给定的应用环境,根据用户的需求,利用数据模型和应用程序模拟现实世界中该应用环境的数据结构和处理活动的过程。
数据库设计原则如下:
(1) 数据库内数据文件的数据组织应获得最大限度的共享、最小的冗余度,消除数据已数据依赖关系中的冗余部分,使依赖于同一个数据模型的数据达到有效的分离。
(2) 保证输入,修改数据时的数据的一致性和正确性。
(3) 保证数据与使用数据的应用程序之间的高度独立性。
1.2.5. 实体与关系
实体是指客观存在并可相互区别的事物,实体即可以是实际的事物,也可以是抽象的概念或关系。
实体之间的3种关系,分别如下。
(1) 一对一关系:是指表A中的一条记录在表B中有且只有一条相匹配的记录,在一对一关系中,大部分相关信息都在一个表中。
(2) 一对多关系:是指表A中的行可以再表B中有许多匹配行,但是表B中的行只能在表A中有一个匹配行。
(3) 多对多关系:是指关系中每个表的行在相关表中具有多个匹配行。在数据库中,多对多关系的建立是依靠第三个表(称作连接表)实现的,连接表包含相关的两个表的主键列,然后从两个相关的表的主键列分别创建与连接表中的匹配列的关系。
1.3. 数据库的体系结构
1.3.1 数据库三级模式结构
数据库的三级模式结构是指模式、外模式和内模式。
\1. 模式
模式也称逻辑模式或概念模式,是数据库中全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。一个数据库只有一个模式。模式处于三级结构的中间层。

\2. 外模式
外模式也称用户模式,是数据库用户(包括应用程序员和最终用户)能够看见和使用的局部数据的逻辑结构和特征的描述,是数据库用户的数据视图,是与某一应用有关的数据的逻辑表示。外模式是模式的子集,一个数据库可以有多个外模式。

\3. 内模式
内模式也是存储模式,是数据物理结构和存储方式的描述,是数据在数据库内部的表示方式。一个数据库只有一个内模式。
1.3.2 三级模式之间的****映射
为了能够在内部实现数据库的3个抽象层次的联系和转化,数据库管理系统在三级模式之间提供了两层映射,分别为外模式/模式映射和模式/内模式映射,下面分别介绍.
\1. 外模式/模式映射
对于同一模式可以有任意多个外模式.对于每一个外模式,数据库系统都有一个外模式/模式映射.当模式改变时,由数据库管理员对各个外模式/模式映射做相应的改变,可以使外模式保持不变,这样,依据数据外模式编写的应用程序就不用修改,保证了数据与程序的逻辑独立性.
\2. 模式/内模式映射
数据库中只有一个模式和一个内模式,所以模式/内模式映射是唯一的,它定义了数据库的全局逻辑结构与存储结构之间的对应关系.当数据库的存储结构改变时,由数据库管理员对模式/内模式映射做相应改变,可以使模式保持不变,应用程序相应地也不做变动.这样,保证了数据与程序的物理独立性.
1.4. 小结
本章主要介绍的是数据库技术中的一些基本概念和原理,其中重点包括数据库技术的发展、数据库系统的组成、数据模型的概念、常见的数据模型、关系数据库的规范化及设计原则、实体与关系、数据库的三级模式结构,以及三级模式之间的映射等内容。其中,常见的数据模型和关系数据库的规范化及设计原则希望读者认真学习,重点掌握。
1.5. 实践与练习
\1. 数据库技术的发展经历了哪3个阶段?
\2. 数据模型通常是由哪3部分组成的?
\3. 常用的数据库数据模型主要有哪几种?
第2章 初始MySQL
MySQL数据库可以称得上是目前运行速度最快的SQL数据库。除了具有许多其他数据库所不具备的功能和选择之外,MySQL数据库还是一种完全免费的产品,用户可以直接从网上下载使用,不必支付任何费用。另外,MySQL数据库的跨平台性也是一大优势。本章将对MySQL数据库的概念、MySQL的特性、应用环境,以及如何安装、配置、启动、连接、断开和停止MySQL服务器进行详细介绍。
通过阅读本章,读者可以:
l 了解MySQL数据库的概念和优势
l 熟悉MySQL的特性
l 掌握如何安装和和配置MySQL服务器
l 掌握启动、连接、断开和停止MySQL服务器的方法
l 了解如何学好MySQL
2.1 了解MySQL
MySQL是目前最为流行的开放源代码的数据库管理系统,是完全网络化的/跨平台的关系型数据库系统,他是由瑞典的MySQL AB公司开发的,由MySQL的初始开发人员David Axmark和Michael “Monty” Windenius 于1995年建立,目前属于Oracle公司.它的象征符号是一只名为Sakila的海豚,代表着MySQL数据库和团队的速度、能力、精确和优秀本质。
MySQL数据库可以称得上是目前运行速度最快的SQL数据库。除了具有许多其他数据库所不具备的功能和选择之外,MySQL数据库还是一种完全免费的产品,用户可以直接从网上下载使用,不必支付任何费用。
2.1.1 MySQL数据库概念
数据库(Database)就是一个存储数据的仓库。为了方便数据的存储和管理,它将数据按照特定的规律存储在磁盘上。通过数据库管理系统,可以有效地组织和管理存储在数据库中的数据。MySQL就是这样的一个关系型数据库管理系统(RDBMS),它可以称得上是目前运行速度最快的SQL数据库管理系统。
2.1.2 MySQL****的优势
MySQL是一款自由软件,任何人都可以从其官方网站下载。MySQL是一个真正的多用户、多进程SQL数据库服务器。它是以客户/服务器结构的实现,由一个服务器守护程序mysqld和很多不同的客户程序和库的组成。它能够快捷、有效和.安全地处理大量的数据。相对于Oracle等数据库来说,MySQL在使用时非常简单。MySQL的主要目标是快捷、便捷和易用。
MySQ被广泛地应用在Internet上的中小型网站中。由于其体积小、速度快、总体拥有成本低,尤其是开放源代码这一特点,成为多数中小型网站为了降低网站总体拥有成本而选择MySQL作为网站数据库的重要指标。
2.1.3 MySQL的发展史
MySQL这个名字的由来已经无从考究了。基本指南以及大量的库和工具采用前缀My,已经有10年以上了;另外,MySQL的创始人之一的Monty Widenius的女儿也叫My。到底哪个是MySQL名字的由来,至今仍是一个迷,包括开发者也不知道。
MySQL的海豚徽标的名字是Sakila,它是MySQL AB公司的创办人从用户在“Dolphin命名”比赛中提供的众多建议中选定的,是由来自非洲斯威士兰的开放源码软件开发人Ambrose Twebaze提出的。根据Ambrose的说法,按斯威士兰的本地语言,女性化名称Sakila源自SiSwati。Sakila也是坦桑尼亚、Arusha地区的一个镇的镇名,靠近Ambrose的母国乌干达。
MySQL从无到有,到技术的不断更新、版本的不断升级,经历了一个漫长的过程,这个过程是实践的过程,是MySQL成长的过程。时至今日,MySQL的版本已经更新到了MySQL5.6。如图2.1所示为MySQL官方网站上的截图,足以反映出MySQL的成长历程。

图2.1 MySQL版本的发展

2.2 MySQL的特性
MySQL是一个真正的多用户、多线程SQL数据库服务器。SQL(结构化查询语言)是世界上最流行的和标准化的数据库语言。下面看一下MySQL的特性。
(1) 使用C和C++语言编写,并使用了多种编译器进行测试,保证源代码的可移植性。
(2) 支持AIX、FreeBSD、HP-UX、Linux、Mac OS、Novell Netware、OpenBSD、OS/2 Wrap、Solaris、Windows等多种操作系统。
(3) 为多种编程语言提供了API。这些编程语言包括C、C++、Python、Jave、Perl、PHP、Eiffel、Ruby、Tcl等。
(4) 支持多线程,充分利用CPU资源。
(5) 优化的SQL查询算法,有效地提高查询速度。
(6) 既能够作为一个单独的应用程序应用在客户端服务器网络环境中,也能够作为一个库而嵌入到其他软件中提供多语言支持,常见的编码如中文的GB2312、BIG5、日文的Shift_JIS等都可以用作数据表名和数据列名。
(7) 提供TCP/IP、ODBC和JDBC等多种数据库连接途径。
(8) 提供用于管理、检查、优化数据库操作的管理工具。
(9) 可以处理拥有上千万条记录的大型数据库。
目前的最新版本是MySQL 5.6,它提供了一组专用功能集,在当今现代化、多功能处理硬件和软件以及中间件构架涌现的环境中,极大地提高了MySQL的性能、可扩展性、可用性。
MySQL5.6融合了MySQL数据库和InnoDB存储引擎的优点,能够提高高性能的数据管理解决方案,包括以下几点。
(1) InnoDB作为默认的数据库存储引擎。
(2) 提升了Windows系统下的系统性能和可扩展性。
(3) 改善性能和可扩展性,全面利用各平台现代多核构架的计算能力。
(4) 提高实用性。
(5) 提高易管理性和效率。
(6) 提高可用性。
(7) 改善检测与诊断性能。
2.3 MySQL的应用环境
MySQL与其他大型数据库(如Oracle、DB2、SQL Server等)相比,确有不足之处,如规模小、功能有限等,但是这丝毫也没有减少它受欢迎的程度。对于每个人使用者和中小型企业来说,MySQL提供的功能已经绰绰有余,而且由于MySQL是开放源代码软件,因此可以大大降低总体拥有成本。
目前Internet上流行的网站构架方式是LAMP(Linux+Apache+MySQL+PHP),即使用Linux作为操作系统,Apache作为Web服务器,MySQL作为数据库,PHP作为服务器端脚本解释器。由于这4个软件都是免费或者开放源代码软件(FLOSS),因此使用这种方式不用花一分钱(除人工成本)就可以建立起一个稳定、免费的网站系统。
2.4 MySQL服务器的安装和配置
MySQL是目前最为流行的开放源码的数据库,是完全网络化的跨平台的关系型数据库系统,它是由MySQL AB公司开发、发布并支持的。任何人都能从Internet上下载MySQL软件,而无需支付任何费用,并且“开放源代码”意味着任何人都可以使用和修改该软件,如果愿意,用户也可以研究源代码并进行恰当的修改,以满足自己的需求,不过需要注意的是,这种“自由”是有范围的。
2.4.1 MySQL服务器下载
MySQL服务器的安装包可以到Oracle的官网(http://www.oracle.com/index.html)中下载。下载MySQL的具体步骤如下。
(1) 在浏览器的地址栏中输入URL地址http://www.oracle.com/index.html,进入Oracle官网首页,将鼠标移动到Downloads上,将显示如图2.2所示的Downloads子菜单。

图2.2 Oracle官网的Downloads子菜单
(2) 在如图2.2所示的菜单中,单击MySQL超链接,进入到Mysql Downloads页面,将页面滚动到底部,如图2.3

图2.3 MySQL Downloads页面
(3) 单击Community(GPL)Downloads>>超链接,进入到MySQL Community Downloads页面,如图2.4所示

图2.4 MySQL Community Downloads页面
(4) 单击MySQL Community Server(GPL)超链接,将进入到Download MySQL Community Server页面,将页面滚动到如图2.5所示的位置

图2.5 Download MySQL Community Server页面
(5) 根据自己的操作系统来选择合适的安装软件,这里以针对Windows 32位操作系统的完整版MySQL Server为例进行介绍,单击图2.5中的图片,将进入到Download MySQL Installer页面,在该页面中,滚动到如图2.6所示的位置

图2.6 Download MySQL Installer页面
(6) 单击Download按钮,将进入到如图2.7所示的Begin Your Download – mysql-installer-community-5.6.20.0.msi页面。

图2.7 Begin Your Download - mysql-installer-community-5.6.20.0.msi页面
(7) 单击No thanks,just start ,y dpwm;pad.超链接,将打开如图2.8所示的文件下载对话框,单击“保存”按钮,下载安装文件。

图2.8 文件下载
2.4.2 MySQL****服务器安装
下载MySQL服务器的安装文件以后,将得到一个名称为mysql-installer-community-5.6.20.0.msi的安装文件,双击该文件可以进行MySQL服务器的安装,具体的安装步骤如下。
(1) 双击下载后的mysql-installer-community-5.6.20.0.msi文件,打开安装向导,如果没有打开安装向导,而是弹出如图2.9所示的对话框,那么还需要先安装.NET4.0框架,然后再重新安装双击下载后的安装文件,打开安装向导对话框,如2.10所示。

图2.9 打开需要安装.NET 4.0框架的提示对话框

图2.10 安装向导对话框
(2) 在打开安装向导对话框中单击Install MySQL Products超链接,将打开License Agreement对话框,询问是否接受协议,选中I accept the license terms复选框,接受协议,如图2.11所示。

图2.11 License Agreement对话框
(3) 单击Next按钮,将打开Find latest products对话框。在该对话框中,选中 Skip the check for updates(not recommended)复选框,这时,原来的Execute按钮将转换为Next按钮,如图2.12所示。
、

图2.12 Find latest products对话框
(4) 单击Next按钮,将打开Choosing a Setup Type对话框,在该对话框中,共包括Developer Default(开发者默认)、Server Only(仅服务器)、Client only(仅客户端)、Full(完全)和Custom(自定义)5种安装类型,这里选择Developer Default,并且将安装路径修改为“C:\Program Files\MySQL\”,数据存放路径修改为“C:\ProgramData\MySQL\MySQL Server 5.6”,如图2.13所示。

图2.13 Choosing a Setup Type对话框
(5) 单击Next按钮,将打开如图2.14所示的Check Requirements对话框,在对话框中检查系统是否具备安装所必须的.NET 4.0框架和Microsoft Visual C++ 2010 32-bit runtime,如果不存在,单击Execute按钮,将在线安装所需插件,安装完成后,将显示如图2.15所示的对话框。

图2.14 未满足全部安装条件时的Check Requirements对话框

图2.15 安装条件已全部满足时的Check Requirements对话框
(6) 单击Next按钮,将打开如图2.16所示的Installation Progress对话框。

图2.16 未安装完成的Installation Progress对话框
(7) 单击Execute按钮,将开始安装,并显示安装进度。安装完成后,将显示如图2.17所示的对话框。

图2.17 安装完成时的Installation Progress对话框
(8) 单击Next按钮,将打开Configuration Overview对话框,在该对话框中单击Next按钮,将打开用于选择服务器的类型的MySQL Server Configuration对话框,在该对话框中共提供了Development Machine(开发者类型)、Server Machine(服务器类型)和Dedicated Machine(致力于MySQL服务类型)。这里选择默认的Development Machine,如图2.18所示。

图2.18 配置服务器类型和网络选项的对话框

(9) 单击Next按钮,将打开用于设置用户和安全的MySQL Server Configuration对话框,在这个对话框中,可以设置root用户的登录密码,也可以添加新用户,这里只设置root用户的登录密码为root,其他采用默认,如图2.19所示。

图2.19 设置用户和安全的MySQL Server Configuration对话框
(10) 单击Next按钮,将打开Configuration Overview对话框,开始配置MySQL服务器,这里采用默认设置,如图2.20所示。

图2.20 配置MySQL服务器
(11) 单击Next按钮,将显示如图2.21所示的界面,提示安装MySQL提供的简单实例。
、

图2.21 提示安装MySQL提供的简单示例
(12) 单击Next按钮,开始安装,安装完成后,将显示如图2.22所示的界面。

图2.22 配置完成界面
(13) 单击Next按钮,将显示如图2.23所示的安装完成界面。取消选中Start MySQL Workbench after Setup复选框,单击Finish按钮,完成MySQL。

图2.23 安装完成对话框
2.4.3启动、连接、断开和停止My****SQL服务器
通过系统服务器和命令提示符(DOS)都可以启动、连接断开和停击MySQL,操作非常简单。下面以Windows7操作系统为例,讲解其具体的操作流程。通常情况下不要停止MySQL服务器,否则数据库将无法使用。
\1. 启动、停止MySQL服务器
启动、停止MySQL服务器,的方法有两种:系统服务器和命令提示符(DOS)。
- 通过系统服务器启动、停止MySQL服务器
如果MySQL设置为Windows服务,则可以通过选择“开始”→“控制面板”→“系统和安全”→“管理工具”→“服务”命令打开Windows服务管理器。在服务器的列表中找到MySQL服务并右键单击,在弹出的快捷菜单中,完成MySQL服务的各种操作(启动、重新启动、停止、暂停和恢复),如图2.24所示。

图2.24 通过系统服务启动、停止MySQL服务器
- 在命令提示符下启动、停止MySQL服务器
单击“开始”菜单,在出现的命令输入框中输入cmd命令,按Enter键打开DOS窗口。在命令提示符下输入:
>net start mysql
此时再按Enter键,启用MySQL服务器。 在命令提示符下输入: \>net stop mysql 按Enter键,即可停止MySQL服务器。在命令提示符下启动、停止MySQL服务器的运行效果如图2.25所示。

图2.25 在命令提示符下启动、停止MySQL服务器
\2. 连接和断开MySQL服务器
下面分别介绍连接和断开MySQL服务器的方法。
- 连接MySQL服务器通过mysql命令实现。在MySQL服务器启动后,选择“开始”→“运行”命令,在弹出的“运行”窗口中输入cmd命令,在Enter键后进入DOS窗口,在命令提示符下输入:


输入完命令语句后,按Enter键即可连接MySQL服务器,如图2.26所示。

图2.26 连接MySQL服务器

如果用户在使用mysql命令连接MySQL服务器时弹出如图2.27所示的信息,那么说明用户未设置系统的环境变量。

图2.27 连接MySQL服务器出错
也就是说,没有将MySQL服务器的bin文件夹位置添加到Windows的“环境变量”→“系统变量”→Path中,从而导致命令不能执行。
下面介绍这个环境变量的设置方法,其步骤如下。
(1) 右键单击“计算机”图标,在弹出的快捷菜单中选择“属性”命令,在弹出的对话框中选择“高级系统设置”,弹出“系统设置”对话框,如图2.28所示。
(2) 在“系统设置”对话框中,选择“高级”选项卡,单击“环境变量”按钮,弹出“环境变量”对话框,如图2.29所示。

图2.28 “系统属性”对话框 图2.29 “环境变量”对话框
图2.29“环境变量”对话框
(3) 在“环境变量”对话框中,定位到“系统变量”中的Path选项,单击“编辑”按钮,将弹出“编辑系统变量”对话框,如图2.30所示。

图2.30 “编辑系统变量”对话框
(4) 在“编辑系统变量”对话框中,将MySQL服务器的bin文件夹位置(C:Program Files\MySQL\MySQL Server 5.6\bin)添加到“变量值”文本框中,注意要用“;”与其他变量值进行分隔,最后,单击“确定”按钮。
环境变量设置完成后,再使用mysql命令即可成功连接MySQL服务器。
- 断开MySQL服务器
连接到MySQL服务器后,可以通过在MySQL提示符下输入exit或者quit命令断开MySQL连接,格式如下。
mysql> quit;
2.4.4 打开MySQL5.6 Command Line Client
MySQL服务器安装完成后,就可以通过其提供的MySQL5.6Command Line Client程序来操作MySQL数据了。这时,必须先打开MySQL5.6Command Line Client程序,并登陆MySQL服务器。下面将介绍具体步骤。
(1) 在“开始”菜单中,选择“所有程序”→MySQL→MySQL Server5.6→MySQL 5.6 Command Line Client命令,将打开MySQL Command Line Client窗口,如图2.31所示。

图2.31 MySQL客户端命令行窗口
(2) 在该窗口中,输入root用户的密码(这里为root),将登录到MySQL服务器,如图2.32所示。

图2.32 登录到MySQL服务器
2.5如何学好MySQL
学好MySQL最重要的是要多练习。笔者将自己学习数据库的方法总结如下。
\1. 多上机实践
想要熟练地掌握数据库,必须经常上机练习。只有在上机实践中才能深刻体会数据库的使用。通常情况下,数据库管理员工作时间越长,其工作经验就越丰富。很多复杂的问题,都可以根据数据库管理员的经验来更好地解决。上机实践过程中。可以将学到的数据库理论知识理解得更加透彻。
\2. 多编写SQL语句
SQL语句是数据库的灵魂。数据库中的很多操作都是通过SQL语句来实现的。只有经常使用SQL语句来操作数据库中的数据,读者才能更加深刻地理解数据库。
\3. 数据库理论知识不能丢
数据库理论知识是学好数据库的基础。虽然理论知识会有点枯燥,但是这是学好数据库的前提。例如,数据库理论中会设计E-R图、数据库设计原则等知识。如果不了解这些知识,就很难独立设计一个很好的数据库及表。读者可以将数据库理论知识与上机实践结合到一起来学习,这样效率会提高。
2.6小 结
本章介绍了数据库和MySQL的基础知识。通过本章的学习,希望读者对数据库、MySQL数据库和SQL 等知识有所了解。而且,希望读者能够了解常用的数据库系统。第3章将介绍在Windows操作系统下安装和配置MySQL,同时对数据库的相关知识也要有一定的了解。
2.7实践与联系
\1. 到Oracle的官网中下载最新版本的MySQL服务器并安装。
\2. 尝试在命令提示符下启动、停止MySQL服务器。
第3章 数据库操作
启动并连接MySQL服务器后,即可对MySQL数据库进行操作,操作MySQL数据库的方法非常简单。本章将对操作MySQL数据库中的创建数据库、修改数据库、查看数据库、选择数据库和删除数据库进行详细介绍。
l 了解数据库的相关知识
l 掌握创建数据库的方法
l 了解查看数据库的方法
l 了解选择数据库的方法
l 掌握修改数据库的方法
l 掌握删除数据库的方法
3.1认识数据库
在进行数据库操作前,首先需要对其有一个基本的了解。本节将对数据库的基本概念、数据库对象及其相关知识进行详细的介绍。
3.1.1 数据库基本概念
数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,是存储在一起的相关数据的集合。其优点主要体现在一下几个方面。
(1) 减少数据的冗余度,节省数据的存储空间;
(2) 具有较高的数据独立性和易扩充性;
(3) 实现数据资源的充分共享。
下面介绍与数据库相关的几个概念。
\1. 数据库系统
数据库系统(DataBase System DBS)是采用数据库技术的计算机系统,是由数据库(数据)、数据库管理系统(软件)、数据库管理人员(人员)、硬件平台(硬件)和软件平台(软件)5部分构成的运行实体。其中,数据库管理员(DataBase Administrator,DBA)是对数据库进行规划、设计、 维护和监视等的专业管理人员,在数据库系统中起着非常重要的作用。
\2. 数据库管理系统
数据库管理系统(DataBase Management System,DBMS)是数据库系统的一个重要组成部分,是位于用户与操作之间的一层数据管理软件,负责数据库中的数据组织、数据操纵、数据维护和数据服务等。主要具有如下功能。
(1) 数据存取的屋里构建:为数据模式的物理存取与构建提供有效的存取方法与手段。
(2) 数据操纵功能:为用户使用数据库的数据提供方便,如查询、插入、修改、删除等以简单的算术运算和统计。
(3) 数据定义功能:用户可以通过数据库管理系统提供的数据定义语言(Data Definition Language,DDL)方便地对数据库中的对象进行定义。
(4) 数据库的运行管理:数据库管理系统统一管理数据库的运行和维护,以保障数据的安全性、完整性、并发性和故障的系统恢复性。
(5) 数据库的建立和维护功能:数据库管理系统能够完成初始数据的输入和转换、数据库的转储和恢复、数据库的性能监视和分析等任务。
\3. 关系数据库
关系数据库是支持关系模型的数据库。关系模型由关系数据结构、关系操作集合和完整性约束3部分组成。
(1) 关系数据结构:在关系模型中数据结构单一,现实世界的实体以及实体间的联系均用关系来表示,实际上关系模型中数据结构就是一张二维表。
(2) 关系操作集合:关系操作分为关系代数、关系演算、具有关系代数和关系演算双重特点的语言(SQL)。
(3) 完整性约束:完整性约束包括实体完整性、参照完整性和用户定义完整性。
3****.12 数据库常用对象
在MySQL的数据库中,表、视图、存储过程和索引等具体存储数据或对数据进行操作的实体都被称为数据库对象。下面介绍几种常用的数据库对象。
\1. 表
表是包含数据库中所有数据的数据库对象,由行和列组成,用于组织和存储数据。
\2. 字段
表中每列称为一个字段,字段具有自己的属性,如字段类型、字段大小等。 其中,字段类型是字段最重要的属性,它决定了字段能够存储那种数据。
\3. 索引
索引是一个单独的、物理的数据库结构。它是依赖于表建立的,在数据库中索引使数据库程序无须对整个表进行扫描,就可以在其中找到所需的数据。
\4. 视图
视图是从一张或者多张表中导出的表(也称虚拟表),是用户查看数据表中的一种方式。表中包括几个被定义的数据列与数据行,其结构和数据建立在对表的查询基础之上。
\5. 存储过程
存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集合(包含查询、插入、删除和更新等操作),经编译后以名称的形式存储在SQL Server服务器端的数据库中,由用户通过指定存储过程的名字来执行。当这个存储过程被调用执行时,这些操作也会同时执行。
3****.13 系统数据库
系统数据库是指安装完MySQL服务器后,会附带一些数据库。例如在默认安装的MySQL Workbench中,会默认创建如图4.1所示的5个数据库,这些数据库就称为系统数据库。系统数据库会记录一些必需的信息,用户是不能直接修改这些系统数据库的。test和sakila除外,这两个数据库中的信息对于系统不是必需的,可以进行修改。下面将对图4.1中所列的系统数据库分别进行介绍。
\1. Information_schema数据库
Information_schema数据库主要用于存储数据库对象的相关信息。例如,用户表信息、列信息、权限信息、字符集信息和分区信息等。
\2. Performance_schema数据库
Performance_schema数据库主要用于存储数据库服务器性能参数。
\3. Sakila数据库
Sakila数据库是MySQL提供的样例数据库。该数据库共有16张数据表,这些数据表都是比较常见的,在设计数据库时,可以参照这些样例数据来快速完成所需的数据表。
\4. test数据库
test数据库时MySQL数据库管理系统自动创建的测试数据库,该数据库中没有创建任何数据表,对于任何用户都可以使用这个数据库。一般情况下,不建议直接使用该数据。
\5. world数据库
world数据库是MySQL数据库管理系统自动创建的数据库,该数据库中只包括3张数据表,分别保存城市、国家和国建使用的语言内容。


图4.1 系统数据库 图4.2用户数据库
3****.2 创建数据库
在MySQL中可以使用CREATE DATABASE语句和CREATE SCHEMA语句创建MySQL数据库,其语法如下。 CREATE {DATABASE|SCHEMA} [IF NOT EXIT]数据库名
[
[DEFAULT]CHARACTER SET [=] 字符集 | [DEFAULT]COLLATE [=] 校对规则名称
];

参数说明如下。
(1) [IF NOT EXITS]:可选项,表示在创建数据库前进行判断,只有该数据库目前尚未存在时才执行创建语句。
(2) 数据库名:必须指定的,在文件系统中,MySQL的数据存储分区将以目录方式表示MySQL数据库。因此,这里的数据库名必须符合操作系统文件夹的命名规则,而在MySQL中是不区分大小写的。
(3) [DEFAULT]:可选项,表示指定默认值。
(4) CHARACTER SET [=] 字符集:可选项,用于指定数据库的字符集。如果不想指定数据库所使用的字符集,那么就可以不使用该项,这时MySQL会根据MySQL服务器默认使用的字符集来创建该数据库。这里的字符集可以使GB2312或者GBK(简体中文)、UTF8(针对Unicode的可变长度的字符编码,也称万国码)、BIG5(繁体中文)、Latin1(拉丁文)等。其中最常用的就是UTF8和GBK。
(5) COLLATE [=]校对规则名称:可选项,用于指定字符集的校对规则。例如,utf8_bin或者 gbk_chinese_ci。具体都有哪些校对规则,可以在MySQL的图形化工具Workbench的创建数据库的窗口中找到,如图4.3所示。
在创建数据库时,数据库命名有以下几项规则。
(1) 不能与其他数据库崇明,否则将发生错误。
(2) 名称可以由任意字母、阿拉伯数字、下划线(_)和“$”组成,可以使用上述的任意字符开头,但不能使用单独的数字,否则会造成它与数值相混淆。
(3) 名称最长可为64个字符,而别名最多可长达256个字符。
(4) 不能使用MySQL关键字作为数据库名、表名。
(5) 默认情况下,在Windows下数据库名、表名的大小写是不敏感的,在Linux下数据库名、表名的大小写是敏感的。为了便于数据库在平台间进行移植,建议读者采用小写
来定义数据库名和表名。
3****.2.1 通过CREATE DATABASE 语句创建基本数据库。
**例****4****.1** 通过CREATE DATABASE语句创建一个名称为db_admin的数据库,具体代码如下。**(实例位置:光盘\****TM****\sl\****4\4.1****)** **create database db_admin;** **]**
运行效果如图4.4所示。 
图4.4 通过CREATE DATABASE语句创建MySQL数据库
3.2.2 通过CREATE SCHEMA****语句创建基本数据库
上面介绍的例如4.1是最基本的创建数据库的方法,实际上,还可以通过语法中给出的CREATE SCHEMA来创建数据库,两者的功能是一样的。在使用MySQL官网中提供的MySQL Workbench 图形化工具创建数据库时,使用的就是这种方法。 **例****4****.2** 通过CREATE SCHEMA语句创建一个名为db_admin1的数据库,具体代码如下。**(实例位置:光盘\****TM****\****sl****\****4****\****4.2****)** **create schema db_admin1;**
运行效果如图4.5所示。

图4.5 通过CREATE SCHEMA语句创建MySQL数据库
3.2.3 创建指定字符集的数据库
在创建数据库时,如果不指定其使用的字符集或者是字符集的校对规则,那么将根据my.ini文件中指定的default-character-set变量的值来设置其使用的字符集。从创建数据库的基本语法中可以看出,在创建数据库时,还可以指定数据库所有的字符集,下面将通过一个具体的例子来演示如何在创建数据库时,指定字符集。 **例4.3** 通过CREATE DATABASE 语句创建一个名称为db_test的数据库,并指定其字符集为GBK,具体代码如下。**(实例位置:光盘****\****TM\sl****\4\4.3)**
CREATE DATABASE db_test
CHARACTER SET=GBK;
运行效果如图4.6所示。

图4.6 创建使用GBK字符集的MySQL数据库
3****.2.4 创建数据库前判断是否存在同名数据库
在MySQL中,不允许同一系统中存在两个相同名称的数据库,如果要创建的额数据库名称已经存在,那么系统将给出以下错误信息。 **ERROR 1007(HY000)****:****Can’t create database** **‘db_test’;database exists;**
为了避免错误的发生,在创建数据库时,可以使用 IF NOT EXISTS选项来实现在创建数据库前判断该数据库是否存在,只有不存在时才会进行创建。
例4.4 通过CREATE DATABASE语句创建一个名称为db_test1的数据库,并在创建前判断该数据库名是否存在,只有不存在时才进行创建,具体代码如下。(实例位置:光盘*TM*sl*4*4.4****)**
CREATE DATABASE IF NOT EXISTS db_test1;
运行效果如图4.7所示。

图4.7 创建数据库前判断是否存在同名数据库
再次执行上面的语句,将不再创建数据库db_test1,显示效果如图4.8所示。

图4.8 创建已经存在的数据库的效果
图4.8 创建已经存在的数据库的效果
3.3查看数据库
成功创建数据库后,可以使用SHOW命令查看MySQL服务器中的所有数据库信息,语法如下。 SHOW {DATABASES|SCHEMAS} [LIKE ‘模式’ WHERE 条件] ;
参数说明如下:
(1) {DATABASES|SCHEMAS}:表示必须有一个是必选项,用于列出当前用户权限范围内所能查看到的所有数据库名称。这两个选项的据俄国是一样的,使用哪个都可以。
(2) LIKEL:可选项,用于指定匹配模式。
(3) WHERE:可选项,用于指定数据库名称查询范围的条件。
例4.5 在4.2.1节中创建了数据库db_admin,下面使用SHOW DATABASES语句查看MySQL服务器中的所有数据库名称,代码如下。(实例位置:光盘*TM*ls*4*4.5****)**
SHOW DATABASES;
运行结果如图4.9所示。

图4.9 查看数据库
从图4.6运行结果可以看出,通过SHOW命令查看MySQL服务器中的所有数据库,结果显示MySQL服务器中有8个数据库,这8个数据库包括系统数据库。
如果MySQL服务器中的数据库比较多,也可以通过指定匹配模式来筛选想要得到的数据库,下面将通过一个具体的实例来演示如何通过LIKE关键字筛选要查看的数据库。
例4.6 筛选以db_开头的数据库名称,代码如下。(实例位置:光盘\TM\sl\4\4.6)
SHOW DATABASES LIKE ‘db_%’;
执行效果如图4.10所示。

图4.10 筛选以db_开头的数据库名称
3.4选择数据库
在MySQL中,使用CREATE DATABASE 语句创建数据库后,该数据库并不会自动成为当前数据库,需要使用MySQL提供的USE语句实现,USE语句可以实现选择一个数据库,使其成为当前数据库。只有使用USE语句指定某个数据库为当前数据库后,才能对该数据库及其存储的数据对象执行操作。USE语句的语法格式如下。 USE 数据库名;

**例4.7** 选择名称为db_admin的数据库,设置其为当前默认的数据库,具体代码如下。**(实例位置:光盘\****TM****\****ls****\****4****\****4.7****)\** **USE db_admin;**
执行结果如图4.11所示。

图4.11 选择数据库
3.5修改数据库
在MySQL中,创建一个数据库后,还可以对其进行修改,不过这里的修改是指可以修改被创建的数据库的相关参数,并不能修改数据库名。修改数据库名不能使用这个语句。修改数据库可以使用ALTER DATABASE或者ALTER SCHEMA语句来实现。修改数据库的语句的语法格式如下。 ALTER {DATABASE | SCHEMA} [数据库名] [DEFAULT] CHARACTER SET [=] 字符集 | [DEFAULT] COLLATER [=] 校对规则名称 参数说明如下。
(1) {DATABASES|SCHEMAS}:表示必须有一个是必选项,这两个选项的结果是一样的,使用哪个都可以。
(2) [数据库名]:可选项,如果不指定要修改的数据库,那么将表示修改当前(默认)的数据库。
(3) [DEFAULT]:可选项,表示指定默认值。
(4) CHARCTER SET [=]字符集:可选项,用于指定数据库的字符集。如果不想指定数据库所使用的字符集,那么就可以不用使用该项,这时MySQL会根据MySQL服务器默认使用的字符集来创建该数据库。这里的字符集可以是GB2312或者GBK(简体中文)、UTF8(针对Unicode 的可变长度的字符编码,也称万国码)、BIG5(繁体中文)、Latin1(拉丁文)等。其中最常用的就是UTF8和GBK。
(5) COLLATE [=] 校对规则名称:可选项,用于指定字符集的校对规则。例如utf8_bin或者gbk_chinese_ci。这与4.2节创建数据库语法中的该从句是相同的。

例4.8 修改例4.1中创建数据库db_admin,设置默认字符集和校对规则,具体代码如下。(实例位置:光盘*TM*ls*4*4.8****)**
ALTER DATABASE db_admin
**DEFAULT CHARACTER SET gbk** **DEFAULT COLLATE gbk_chinese_ci;**
执行结果如图4.12所示。

图4.12 设置默认字符集和校对规则
3****.6 删除数据库
在MySQL中,可以通过使用DROP DATABASE语句或者DROP SCHEMA语句来删除已经存在的数据库。使用该命令删除数据库的同时,该数据库中的表,以及表中的数据也将永久删除, 因此,在使用该语句删除数据库时一定要小心,以免误删除有用的数据库。DROP DATABASE 或者DROP SCHEMA语句的语法格式如下。 DROP {DATABASE|SCHEMA} [IF EXISTS] 数据库名; 参数说明如下。
(1) {DATABASES|SCHEMAS}:表示必须有一个是必选项,这两个选项的结果是一样的,使用哪个都可以。
(2) [IF EXISTS]:用于指定在删除数据前,先判断该数据库是否已经存在,只有已经存在时,才会执行删除操作,这样可以避免删除不存在的数据库时,产生异常。



例4.9 通过DROP DATABASE 语句删除名为db_admin的数据库,具体代码如下。(实例位置:光盘*TM*ls*4*4.9***)*
DROP DATABASE db_admin;
执行效果如图4.13所示。

图4.13 删除数据库
当使用上面的命令删除数据库时,如果指定的数据库不存在,将产生如图4.14所示的异常信息。 
图4.14 删除不存在的数据库出错
为了解决这一问题,可以在DROP DATABASE语句中使用IF EXISTS从句来保证只有当数据库存在时才执行删除数据库的操作。下面通过一个具体的例子来演示这一功能。 **例4.10** 通过DROP DATABASE语句删除名称为db_111的数据库(该数据库不存在),具体代码如下。**(实例位置:光盘\****TM****\ls\****4****\****4.10****)\** **SHOW DATABASES LIKE** **‘db_%’**
DROP DATABASE IF EXISTS db_111;
执行效果如图4.15所示。

图4.15 删除不存在的数据库未出错

3****.7 小 结
本章首先介绍了数据库的基本概念、数据库的常用对象,以及MySQL中的系统数据库,然后介绍了如何修改数据库、查看数据库、选择数据库和删除数据库。其中,创建数据库、选择数据库和删除数据库需要重点掌握,在实际开发中经常会应用到。
3.8 实践与练习
\1. 通过CREATE SCHEMA语句创建一个名称为db_mr的数据库,并指定其字符集为UTF8。(答案位置:光盘*TM*ls*4*4.11****)
\2. 通过DROP SCHEMA语句删除第1题中创建的数据库db_mr,并且指定只有该数据库存在时才删除。(答案位置:光盘*TM*ls*4*4.12****)**
\3. 通过SHOW SCHEMAS语句筛选以db_开头的数据库名称。(答案位置:光盘*TM*ls*4*4.13****)**
第4章 存储引擎及数据类型
使用存储引擎可以加快查询的速度,并且每一种引擎都存在不同的含义。MySQL的数据类型是数据的一种属性,其可以决定数据的存储格式、有效范围、和相应的限制,并且可以让读者了解如何选择合适的数据类型。本章将对MySQL的存储引擎和数据类型的使用进行详细的讲解
通过阅读本章,读者可以:
l 了解MySQL存储引擎
l 了解并查询MySQL中支持的存储引擎
l 掌握选择存储引擎的方法
l 掌握设置数据表的存储引擎的方法
l 掌握MySQL的数据类型
4.1 MySQL****存储引擎
存储引擎其实就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为在关系数据库中数据是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)。在Oracle和SQL Server等数据库中只有一种存储引擎,所有数据存储管理机制都是一样的;而MySQL数据库提供了多种存储引擎。用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据需要编写自己的存储引擎。
4****.1.1 MySQ****L存储引擎的概念
MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水并且最终提供广泛的、不同功能和能力。通过选择不同的技术,能够获得额外的速度或者功能,从而改善应用的整体功能。
这些不同的技术以及配套的相关功能在MySQL中被称作存储引擎(也称作表类型)。MySQL默认配置了许多不同的存储引擎,可以预先设置或者在MySQL服务器中启用。可以选择适用于服务器、数据库和表格的存储引擎,以便在选择如何存储信息、如何检索这些信息以及需要的数据结合什么性能和功能的时候为其提供最大的灵活性。
4.1.****2 查询MySQL****中支持的存储引擎
\6. 查询支持的全部存储引擎
在MySQL中,可以使用SHOW ENGINES语句查询MySQL中支持的存储引擎。其查询语句如下:
SHOW ENGINES;
**SHOW ENGINES**语句可以使用“;”结束,也可以使用“\g”或者“\G”结束。“\g”与“;”的作用是相同的,“\G”可以让结果显示的更加美观。 使用 **SHOW ENGINES****\g**语句查询的结果如图5.1所示。

图5.1 使用SHOW ENGINES \G语句查询MySQL中支持的存储引擎
使用**SHOW ENGINES\G**语句查询的结果如图5.2所示。

图5.2 使用SHOW ENGINES \G语句查询MySQL中支持的存储引擎
查询结果中的Engine参数指的是存储引擎的名称;Support参数指的是MySQL是否支持该类引擎,YES表示支持,Comment参数指对该引擎的评论。
从查询结果中可以看出,MySQL支持多个存储引擎,其中InnoDB为默认存储引擎。
\7. 查询默认的存储引擎
如果想要知道当前MySQL服务器采用的默认存储引擎是什么,可以通过执行SHOW VARIABLES命令来查看。查询默认的存储引擎的SQL语句如下。
SHOW VARIABLES LIKE ‘stirage_engine%’;
**例5.1** 查询默认的存储引擎,具体代码如下。(**实例位置:光盘\TM\sl\5\5.1**) SHOW VARIABBLES LIKE ‘storage_engine%’; 执行效果如图5.3所示

图5.3 查询默认的存储引擎
从图5.3中可以看出,当前MySQL服务器采用的默认存储引擎是InnoDB。 有些表根本不用来存储长期数据,实际上用户需要完全在服务器的RAM或者特殊的临时文件中创建和维护这些数据,以确保高性能,但这样也存在很高的不稳定风险。还有一些表只是为了简化对一组相同表的维护和访问,为同时与所有这些表交互提供一个单一接口。另外,还有其他一些特别用途的表,但重点是:MySQL支持很多类型的表,每种类型都有自己特定的作用、优点和缺点。MySQL还相应的提供了很多不同的存储引擎,可以以最适合于应用需求的方式存储数据。MySQL有多个可用存储引擎,下面主要介绍InnoDB、MyISAM和MEMORY3种存储引擎。
4.1.****3 InnoDB存储引擎
InnoDB已经开发了十余年,遵循CNU通用公开许可(GPL)发行。InnoDB已经被一些重量级Internet公司所采用,如雅虎、Slashdot和Google,为用户操作非常大的数据库提供了一个强大的解决方案。InnoDB给MySQL的表提供给了事物、回滚、崩溃修复能力和多版本并发控制的事物安全。在MySQL从3.23.34a开始包含InnoDB存储引擎。InnoDB是MySQL上第一个提供外键约束的表引擎。并且InnoDB对事物处理的能力,也是MySQL其他引擎所无法与之比拟的。下面介绍InnoDB存储引擎的特点及其优缺点。
InnoDB存储引擎种支持自动增长列AUTO_INCREMENT。自动增长列的值不能为空,且值必须唯一。MySQL中规定自增列必须为主键。在插入值时,如果自动增长列不输入值,则插入值为自动增长后的值;如果输入的值为0或者空(NULL),则插入的值也为自动增长后的值;如果插入某个确定的值,且该值在前面没有出现过,则可以直接插入。
InnoDB存储引擎种在支持外键(FOREIGN KEY)。外键所载的表为子表,外键所依赖的表为父表,父表中被子表外键关联的字段必须为主键。当删除、更新父表的某条信息时,子表也必须有相应的改变。InnoDB存储引擎中,创建的表的表结构存储在.frm文件中。数据和索引存储在innodb_data_home_dir和innodb_data_file_path表空间中。
InnoDB存储引擎的优势在于提供了良好的事物管理、崩溃修复能力和并发控制。缺点是其读写效率较差,占用的数据空间相对比较大。
InnoDB表是如下情况的理想引擎:
(1) 更新密集的表:InnoDB存储引擎特别适合处理多重并发的更新请求。
(2) 事物:InnoDB存储引擎是唯一支持事物的标准MySQL存储引擎, 这是管理敏感数据(如金融信息和用户注册信息)的必须软件。
(3) 自动灾难恢复:与其他存储引擎不同,InnoDB表能够自动从灾难中恢复。虽然MyISAM表能在灾难后修复,但其过程要长得多。
Oracle的InnoDB存储引擎广泛应用于基于MySQL的Web、电子商务、金融系统、健康护理以及零售应用。因为InnoDB可提供高效的ACID独立性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)兼容事物处理能力,以及独特的高性能和具有可扩展性的构架要素。
另外,InnoDB设计用于事物处理应用,这些应用需要处理崩溃恢复、参照完整性、高级别的用户并发数,以及响应时间超时服务水平。在MySQL5.5中,最显著的增强性能是将InnoDB作为默认的存储引擎。在MyISAM以及其他表类型依然可用的情况下,用户无需更改配置,就可构建基于InnoDB的应用程序。
4.1.4 MyISAM存储引擎
MyISAM存储引擎是MySQL中常见的存储引擎,曾是MySQL的默认存储引擎。MyISAM存储引擎是基于ISAM存储引擎发展起来的,它解决了ISAM的很多不足。MyISAM增加了很多有用的扩展。
\1. MyISAM存储引擎的文件类型
MyISAM存储引擎的表存储成3个文件。文件的名字与表名相同,扩展名包括frm、MYD和MYI。
(1) frm:存储表的结构。
(2) MYD:存储数据,是MYData的缩写。
(3) MYI:存储索引,是MYIndex的缩写。
\2. MyISAM****存储引擎的存储格式
基于MyISAM存储引擎的表支持3种不同的存储格式,包括静态型、动态型和压缩型。
- MyISAM静态型
如果所有表列的大小都是静态的(即不使用xBLOB、xTEXT或者VARCHAR数据类型),MySQL就会自动使用静态MyISAM格式。使用这种类型的表性能非常高,因为在维护和访问以预定义格式存储的数据时需要很低的开销。但是,这项优点要以空间为代价,因为每列都需要分配给该列最大空间,而无论该空间是否真正地使用。
- MyISAM动态
如果有列(即使只有一列)定义为动态的(使用xBLOB、xTEXT或VARCHAR),MySQL就会自动使用动态格式。虽然MyISAM动态表占用的空间比静态格式所占空间少,但空间的节省带来了性能的下降。如果某个字段的内容发生改变,则其位置很可能就需要移动,这会导致碎片的产生。随着数据集中的碎片增加,数据访问性能就会相应降低。这个问题有以下两种修复方法。
(1) 尽可能使用静态数据类型
(2) 经常使用OPTIMIZE TABLE语句,它会整理表的碎片,恢复由于表更新和删除而导致的空间丢失。
(3) MyISAM压缩
有时会创建在整个应用程序生命周期中都只读的表。如果是这种情况,就可以使用myisampack工具将其转换为MyISAM压缩表来减少空间。在给定硬件配置下(如快速的处理器和低速的硬盘驱动器),性能的提升将相当显著。
\3. MyISAM存储引擎的优缺点
MyISAM存储引擎的优势在于占用空间小,处理速度快;缺点是不支持事务的完整性和并发性。
4****.1.5 MEMORY存储引擎
MEMORY存储引擎是MySQL中的一类特殊的存储引擎。其使用存储在内存中的内容来创建表,而且所有数据也放在内存中。这些特性都与InnoDB存储引擎、MyISAM存储引擎不同。下面将对MEMORY存储引擎的文件存储形式、索引类型、存储周期和优缺点等进行讲解。
1.MEMORY存储引擎的文件存储形式
每个基于MEMORY存储引擎的表实际对应一个磁盘文件。该文件的文件名与表名相同,类型为frm。该文件中只存储表的结构,而其数据文件都是存储在内存中。这样有利于对数据的快速处理,提高整个表的处理效率。值得注意的是,服务器需要有足够的内存来维持MEMORY存储引擎的表的使用。如果不需要使用了,可以释放这些内容,甚至可以删除不需要的表。
2.MEMORY存储引擎的索引类型
MEMORY存储引擎默认使用哈希(HASH)索引,其速度要比使用B树(BTREE)索引快。如果读者希望使用B树索引,可以在创建索引时选择使用。
3.MEMORY存储引擎的存储周期
MEMORY存储引擎通常很少用到。因为MEMORY表的所有数据是存储在内存上的,如果内存出现异常就会影响到数据的完整性。如果重启机器或者关机,表中的所有数据将消失。因此,基于MEMORY存储引擎的表生命周期很短,一般都是一次性的。
4.MEMORY存储引擎的优缺点
MEMORY表的大小是受到限制的。表的大小主要取决于两个参数,分别是max_rows和max_heap_table_size。其中,max_rows可以在创建表时指定;max_heap_table_size的大小默认为16MB,可以按需要进行扩大。因此,其存在于内存中的特性,决定了这类表的处理速度非常快。但是,其数据易丢失,生命周期短。
创建MySQL MEMORY存储引擎的出发点是速度。为得到最快的响应时间,采用的逻辑存储介质是系统内存。虽然在内存中存储表数据确实会提高性能,但要记住,当mysqld守护进程崩溃时,所有的MEMORY数据都会丢失。
MEMORY表不支持VARCHAR、BLOB和TEXT数据类型,因为这种表类型按固定长度的记录格式存储。此外,如果使用版本4.1.0之前的MySQL,则不支持自动增加列(通过AUTO_INCREMENT属性)。当然,要记住MEMORY表只用于特殊的范围,不会用于长期存储数据。基于其这个缺陷,选择MEMORY存储引擎时要特别小心。
当数据有如下情况时,可以考虑使用MEMORY表。
(1)暂时:目标数据只是临时需要,在其生命周期中必须立即可用。
(2)相对无关:存储在MEMORY表中的数据如果突然丢失,不会对应用服务产生实质的负面影响,而且不会对数据完整性有长期影响。
如果使用MySQL 4.1及其之前版本,MEMORY的搜索比MyISAM表的搜索效率要低,因为MEMORY表只支持散列索引,这需要使用整个键进行搜索。但是,4.1之后的版本同时支持散列索引和B树索引。B树索引优于散列索引的是,可以使用部分查询和通配查询,也可以使用<、>和>=等操作符方便数据挖掘。
4****.1.6 如何选择存储引擎
每种存储引擎都有各自的优势,不能笼统地说谁比谁更好,只有适合不适合。下面根据其不同的特性,给出选择存储引擎的建议。
(1)InnoDB存储引擎:用于事务处理应用程序,具有众多特性,包括ACID事务支持,支持外键。同时支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高,要求实现并发控制,那选择InnoDB存储引擎有其很大的优势。如果需要频繁地进行更新、删除操作的数据库,也可以选择InnoDB存储引擎,因为该类存储引擎可以实现事务的提交(Commit)和回滚(Rollback)。
(2)MyISAM存储引擎:管理非事务表,它提供高速存储和检索,以及全文搜索能力。MyISAM存储引擎插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM存储引擎能实现处理的高效率。如果应用的完整性、并发性要求很低,也可以选择MyISAM存储引擎。
(3)MEMORY存储引擎:MEMORY存储引擎提供“内存中”的表,MEMORY存储引擎的所有数据都在内存中,数据的处理速度快,但安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMORY存储引擎。MEMORY存储引擎对表的大小有要求,不能建太大的表。所以,这类数据库只使用相对较小的数据库表。
以上存储引擎的选择建议是根据不同存储引擎的特点提出的,并不是绝对的。实际应用中还需要根据各自的实际情况进行分析。
5.1.7 设置数据表的存储引擎
下面创建db_database03数据库文件,在数据库中创建3个数据表,并分别为其设置不同的存储引擎。以此来诠释这3种不同存储引擎创建的数据表文件有什么区别。
(1)创建tb_001数据表,设置存储引擎为MyISAM,生成的数据表文件如图5.4所示,由3个不同后缀的文件组成。

图5.4 创建tb_001数据表及生成的数据表文件
(2)创建tb_002数据表,设置存储引擎为MEMORY,生成的数据表文件如图5.5所示,只有一个后缀为frm的文件。
7
图5.5 创建tb_002数据表及生成的数据表文件
(3)创建tb_003数据表,设置存储引擎为InnoDB,生成的数据表文件如图5.6所示,同样也由一个后缀为frm的文件组成。

图5.6 创建tb_003数据表及生成的数据表文件
4****.2 MySQL数据类型
在MySQL数据库中,每一条数据都有其数据类型。MySQL支持的数据类型主要分成3类:数字类型、字符串(字符)类型、日期和时间类型。
4****.2.1 数字类型
MySQL支持所有的ANSI/ISO SQL 92数字类型。这些类型包括准确数字的数据类型(NUMERIC、DECIMAL、INTEGER和SMALLINT),还包括近似数字的数据类型(FLOAT、REAL和DOUBLE PRECISION)。其中的关键词INT是INTEGER的同义词,关键词DEC是DECIMAL的同义词。
数字类型总体可以分成整型和浮点型两类,详细内容如表5.1和表5.2所示。
表5.1 整数数据类型

表5.2 浮点数据类型

4****.2.2 字符串类型
字符串类型可以分为3类:普通的文本字符串类型(CHAR和VARCHAR)、可变类型(TEXT和BLOB)和特殊类型(SET和ENUM)。它们之间都有一定的区别,取值范围不同,应用的地方也不同。
(1)普通的文本字符串类型,即CHAR和VARCHAR类型,CHAR列的长度被固定为创建表所声明的长度,取值在1~255之间;VARCHAR列的值是变长的字符串,取值和CHAR一样。普通的文本字符串类型的介绍如表5.3所示。
表5.3 常规字符串类型

(2)可变类型(TEXT和BLOB)。它们的大小可以改变,TEXT类型适合存储长文本,而BLOB类型适合存储二进制数据,支持任何数据,如文本、声音和图像等。TEXT和BLOB类型的介绍如表5.4所示。
表5.4 TEXT和BLOB类型
(3)特殊类型(SET和ENUM)。
特殊类型(SET和ENUM)的介绍如表5.5所示。
表5.5 ENUM和SET类型

4****.2.3 日期和时间类型
日期和时间类型包括:DATETIME、DATE、TIMESTAMP、TIME和YEAR。其中的每种类型都有其取值的范围,如赋予它一个不合法的值,将会被“0”代替。日期和时间类型的介绍如表5.6所示。
表5.6 日期和时间数据类型
在MySQL中,日期的顺序是按照标准的ANSI SQL格式进行输出的。
4****.3 小结
本章对MySQL存储引擎和数据类型分别进行了详细讲解,并通过举例说明,帮助读者更好地理解所学知识的用法。在阅读本章时,读者应该重点掌握什么类型的表适合什么类型的存储引擎,同时对MySQL中的数据类型也要有一定的了解,在以后设计数据表时,能够合理地选择所使用的数据类型。
4****.4 实践与练习
1.查询MySQL中支持的存储引擎,并且以友好效果进行显示。(答案位置:光盘\TM\sl\5\5.2)
2.查询默认的存储引擎,并且以友好效果进行显示。(答案位置:光盘\TM\sl\5\5.3)
第5章 操作数据表
在对MySQL数据表进行操作之前,必须首先使用USE语句选择数据库,才可在指定的数据库中对数据表进行操作,如创建数据表、修改表结构、数据表更名或删除数据表等;否则是无法对数据表进行操作的。本章将对数据表的操作方法进行详细介绍。
通过阅读本章,读者可以:
l 掌握创建数据表的方法
l 了解查看数据表的方法
l 掌握修改数据表结构的方法
l 掌握重命名、复制和删除数据表的方法
5****.1 创建数据表
创建数据表使用CREATE TABLE语句。语法如下。
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] 数据表名
[(create_definition,…)][table_options] [select_statement]
CREATE TABLE语句的参数说明如表6.1所示。
表6.1 CREATE TABLE语句的参数说明
下面介绍列属性create_definition部分,每一列定义的具体格式如下。
col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT]
[PRIMARY KEY ] [reference_definition]
属性create_definition的参数说明如表6.2所示。
表6.2 属性create_definition的参数说明
以上是创建一个数据表的一些基础知识,它看起来十分复杂,但在实际的应用中使用最基本的格式创建数据表即可,具体格式如下。
CREATE TABLE 数据表名 (列名1 属性,列名2 属性…);
例6.1使用CREATE TABLE语句在MySQL数据库db_admin中创建一个名为tb_admin的数据表,该表包括id、user、password和createtime等字段,具体代码如下。(实例位置:光盘\TM\sl\6\6.1)
USE db_admin;
CREATE TABLE tb_admin(
id int auto_increment primary key,
user varchar(30) not null,
password varchar(30) not null,
createtime datetime);
执行结果如图6.1所示。

图6.1 创建MySQL数据表
在完成本实例前,如果不存在名称为db_admin的数据库,那么需要先创建该数据库,创建数据库db_admin的具体代码如下。
CREATE DATABASE db_admin;
5****.2 查看表结构
对于一个创建成功的数据表,可以使用SHOW COLUMNS语句或DESCRIBE语句查看指定数据表的表结构。下面分别对这两个语句进行介绍。
5****.2.1 使用SHOW COLUMNS语句查看
在MySQL中,使用SHOW COLUMNS语句可以查看表结构,SHOW COLUMNS语句的基本语法格式如下。
SHOW [FULL] COLUMNS FROM 数据表名 [FROM 数据库名];
或
SHOW [FULL] COLUMNS FROM 数据表名.数据库名;
例6.2使用SHOW COLUMNS语句查看数据表tb_admin的表结构,具体代码如下。(实例位置:光盘\TM\sl\6\6.2)
SHOW COLUMNS FROM tb_admin FROM db_admin;
执行效果如图6.2所示。

图6.2 查看表结构
5****.2.2 使用DESCRIBE语句查看
在MySQL中,还可以使用DESCRIBE语句查看数据表结构。DESCRIBE语句的基本语法格式如下。
DESCRIBE 数据表名;
其中,DESCRIBE可以简写成DESC。在查看表结构时,也可以只列出某一列的信息。其语法格式如下。
DESCRIBE 数据表名列名;
例6.3使用DESCRIBE语句的简写形式查看数据表tb_admin中的某 一列信息。(实例位置:光盘\TM\sl\6\6.3)
(1)编写SQL语句,选择要查看数据表所在的数据库,具体代码如下。
USE db_admin;
(2)应用简写的DESC命令查看数据表tb_admin中的user字段的信息,具体代码如下。
DESC tb_admin user;
执行结果如图6.3所示。

图6.3 查看表的某一列信息
5****.3 修改表结构
修改表结构使用ALTER TABLE语句。修改表结构指增加或者删除字段、修改字段名称或者字段类型、设置取消主键外键、设置取消索引以及修改表的注释等,语法如下。
ALTER [IGNORE] TABLE 数据表名 alter_spec[,alter_spec]…| table_options
参数说明如下。
(1)[IGNORE]:可选项,表示如果出现重复关键的行,则只执行一行,其他
重复的行被删除。
(2)数据表名:用于指定要修改的数据表的名称。
(3)alter_spec子句:用于定义要修改的内容,其语法格式如下。
ADD [COLUMN] create_definition [FIRST | AFTER column_name ] //添加新字段
| ADD INDEX [index_name] (index_col_name,...) //添加索引名称
| ADD PRIMARY KEY (index_col_name,...) //添加主键名称
| ADD UNIQUE [index_name] (index_col_name,...) //添加唯一索引
| ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT} //修改字段默认值
| CHANGE [COLUMN] old_col_name create_definition //修改字段名/类型
| MODIFY [COLUMN] create_definition //修改子句定义字段
| DROP [COLUMN] col_name //删除字段名称
| DROP PRIMARY KEY //删除主键名称
| DROP INDEX index_name //删除索引名称
| RENAME [AS] new_tbl_name //更改表名
上面的语法中,各参数说明如下。
① create_definition:用于定义列的数据类型和属性,与6.1节CREATE TABLE语句中的语法相同。
② [FIRST | AFTER column_name ]:用于指定位于哪个字段的前面或者后面,当使用FIRST关键字时,表示位于指定字段的前面;使用AFTER关键字时,表示位于指定字段的后面。其中的column_name表示字段名。
③ [index_name]:可选项,用于指定索引名。
④ (index_col_name,...):用于指定索引列名。
⑤ {SET DEFAULT literal | DROP DEFAULT}子句:为字段设置或者删除默认值。其中literal参数为要设置的默认值。
⑥ old_col_name:用于指定要修改的字段名。
⑦ new_tbl_name:用于指定新的表名。
(4)table_options:用于指定表的一些特性参数,其中大多数选项涉及的是表数据如何存储及存储在何处,如ENGINE选项用于定义表的存储引擎。多数情况下,用户不必指定表选项。
ALTER TABLE语句允许指定多个动作,其动作间使用逗号分隔,每个动作表示对表的一个修改。
5****.3.1 添加新字段及修改字段定义
在MySQL的ALTER TABLE语句中,可以通过使用ADD [COLUMN] create_definition [FIRST |AFTER column_name ]子句来添加新字段;用MODIFY[COLUMN] create_definition子句可以修改已定义字段的定义。下面将通过一个具体实例演示如何为一个已有表添加新字段,并修改已有字段的字段定义。
例6.4添加一个新的字段email,类型为varchar(50),not null,将字段user的类型由varchar(30)改为varchar(40)。(实例位置:光盘\TM\sl\6\6.4)
(1)选择数据库db_admin,具体代码如下。
USE db_admin;
(2)编写SQL语句,实现向数据表tb_admin中添加一个新字段,并且修改字段user的类型,具体代码如下。
ALTER TABLE tb_admin ADD email varchar(50) not null ,
modify user varchar(40);
在命令行模式下的运行情况如图6.4所示。

图6.4 添加新字段、修改字段类型
(3)通过DESC命令查看数据tb_user的表结构,以查看表结构是否成功修改,具体代码如下。
DESC tb_admin;
执行效果如图6.5所示。

图6.5 修改后tb_user的表结构
通过ALTER语句修改表列,其前提是必须将表中数据全部删除,然后才可以修改表列。
5****.3.2 修改字段名
在MySQL的ALTER TABLE语句中,使用CHANGE [COLUMN] old_col_name create_definition子句可以修改字段名或者字段类型。下面将通过一个具体实例演示如何修改字段名。
例6.5将数据表tb_userNew1的字段名user修改为username,具体代码如下。(实例位置:光盘\TM\sl\6\6.5)
ALTER TABLE db_admin.tb_usernew1
CHANGE COLUMN user username VARCHAR(30) NULL DEFAULT NULL ;
执行效果如图6.6所示。

图6.6 修改字段名
5****.3.3 删除字段
在MySQL的ALTER TABLE中,使用DROP [COLUMN] col_name子句可以删除指定字段。下面将通过一个具体实例演示如何删除字段。
例6.6将数据库db_admin中的数据表tb_userNew1更名为tb_userOld。(实例位置:光盘\TM\sl\6\6.6)
(1)选择数据库db_admin,具体代码如下。
USE db_admin;
(2)编写SQL语句,实现将数据表tb_admin中的字段email删除,具体代码如下。
ALTER TABLE tb_admin DROP email;
在命令行模式下的运行情况如图6.7所示。

图6.7 删除字段
5****.3.4 修改表名
在MySQL的ALTER TABLE中,使用RENAME [AS] new_tbl_name子句可以修改表名。下面将通过一个具体实例演示如何修改表名。
例6.7将数据库db_admin中的数据表tb_userNew1更名为tb_userOld。(实例位置:光盘\TM\sl\6\6.7)
(1)选择数据库db_admin,具体代码如下。
USE db_admin;
(2)编写SQL语句,实现将数据表tb_userNew1更名为tb_userOld,具体代码如下。
ALTER TABLE tb_usernew1 RENAME AS tb_userOld;
在命令行模式下的运行情况如图6.8所示。

图6.8 修改表名
5****.4 重命名表
在MySQL中,重命名数据表可以使用RENAME TABLE语句来实现。RENAME TABLE语句的基本语法格式如下。
RENAME TABLE 数据表名1 To 数据表名2
该语句可以同时对多个数据表进行重命名,多个表之间以逗号“,”分隔。
例6.8对数据表tb_admin进行重命名,更名后的数据表为tb_user。(实例位置:光盘\TM\sl\6\6.8)
(1)使用RENAME语句将数据表tb_admin重命名为tb_user,具体代码如下。
RENAME TABLE tb_admin TO tb_user;
(2)重命名后,应用DESC语句查看数据表tb_user的表结构,具体代码如下。
DESC tb_user;
执行效果如图6.9所示。

图6.9 对数据表进行更名
5****.5 复制表
创建表的CREATE TABLE命令还有另外一种语法结构,在一张已经存在的数据表的基础上创建一份该表的备份,也就是复制表。这种用法的语法格式如下。
CREATE TABLE [IF NOT EXISTS] 数据表名
{LIKE 源数据表名 | (LIKE 源数据表名)}
参数说明如下。
(1)[IF NOT EXISTS]:可选项,如果使用该子句,表示当要创建的数据表名不存在时,才会创建。如果不使用该子句,当要创建的数据表名存在时,将出现错误。
(2)数据表名:表示新创建的数据表的名,该数据表名必须是在当前数据库中不存在的表名。
(3){LIKE源数据表名| (LIKE源数据表名)}:必选项,用于指定依照哪个数据表来创建新表,也就是要为哪个数据表创建副本。
使用该语法复制数据表时,将创建一个与源数据表相同结构的新表,该数据表的列名、数据类型空指定和索引都将被复制,但是表的内容是不会复制的。因此,新创建的表是一张空表。如果想要复制表中的内容,可以通过使用AS(查询表达式)子句来实现。
例6.9在数据库db_admin中创建一份数据表tb_user的备份tb_userNew。(实例位置:光盘\TM\sl\6\6.9)
(1)选择数据表所在的数据库db_admin,具体代码如下。
USE db_admin;
(2)创建一份数据表tb_user的备份tb_userNew,具体代码如下。
CREATE TABLE tb_userNew
LIKE tb_user;
执行效果如图6.10所示。

图6.10 创建一份数据表tb_user的备份tb_userNew
(3)查看数据表tb_user和tb_userNew的表结构,具体代码如下。
DESC tb_user;
DESC tb_userNew;
执行结果如图6.11所示。

图6.11 查看数据表tb_user和tb_userNew的表结构
从图6.11中可以看出,数据表tb_user和tb_userNew的表结构是一样的。
(4)分别查看数据表tb_user和tb_userNew的内容,具体代码如下。
SELECT * FROM tb_user;
SELECT * FROM tb_userNew;
执行效果如图6.12所示。

图6.12 查看数据表tb_user和tb_userNew的内容
从图6.12中可以看出,在复制表时并没有复制表中的数据。
(5)如果在复制数据表时,想要同时复制其中的内容,那么需要使用下面的代码来实现。
CREATE TABLE tb_userNew1
AS SELECT * FROM tb_user;
执行结果如图6.13所示。

图6.13 复制数据表同时复制其中的数据
(6)查看数据表tb_userNew1中的数据,具体代码如下。
SELECT * FROM tb_userNew1;
执行效果如图6.14所示。

图6.14 查看新复制的数据表tb_userNew1的数据
从图6.14中可以看出,在复制表的同时也复制了表中的数据。
5****.6 删除表
删除数据表的操作很简单,同删除数据库的操作类似,使用DROP TABLE语句即可实现。DROP TABLE语句的基本语法格式如下。
DROP TABLE [IF EXISTS] 数据表名;
参数说明如下。
(1)[IF EXISTS]:可选项,用于在删除表前先判断是否存在要删除的表,只有存在时,才执行删除操作,这样可以避免要删除的表不存在时出现错误信息。
(2)数据表名:用于指定要删除的数据表名,可以同时删除多张数据表,多个数据表名之间用英文半角的逗号“,”分隔。
例6.10删除数据表tb_user。(实例位置:光盘\TM\sl\6\6.10)
(1)选择数据表所在的数据库db_admin,具体代码如下。
USE db_admin;
(2)应用DROP TABLE语句删除数据表tb_user,具体代码如下。
DROP TABLE;
执行效果如图6.15所示。

图6.15 删除数据表
删除数据表的操作应该谨慎使用。一旦删除了数据表,那么表中的数据将会全部清除,没有备份则无法恢复。
在删除数据表的过程中,删除一个不存在的表将会产生错误,如果在删除语句中加入IF EXISTS关键字就不会出错了,格式如下。
DROP TABLE IF EXISTS 数据表名;
5****.7 小结
本章主要介绍了如何创建数据表、查看表结构、修改表结构、重命名表、复制表和删除表等内容。其中,创建和修改表这两部分内容比较重要,需要不断的练习才会对这两部分了解得更加透彻。而且,这两部分很容易出现语法错误,必须在练习中掌握正确的语法规则。创建表和修改表后一定要查看表的结构,这样可以确认操作是否正确。删除表时一定要特别小心,因为删除表的同时会删除表中的所有数据。
5****.8 实践与练习
1.编写SQL语句,实现查看数据表tb_admin的表结构。(答案位置:光盘\TM\sl\6\6.11)
2.编写SQL语句,实现为数据表tb_userNew的user字段设置默认值为mr。(答案位置:光盘\TM\sl\6\6.12)
3.编写SQL语句,实现当数据表tb_user存在的情况下删除该数据表。(答案位置:光盘\TM\sl\6\6.13)
第6章 MySQL基础
同其他语言一样,MySQL数据库也有自己的运算符和流程控制语句。本章将对MySQL的运算符和流程控制语句进行详细介绍。
通过阅读本章,读者可以:
l 掌握MySQL的运算符
l 掌握MySQL的流程控制
6****.1 运算符
6****.1.1 算术运算符
算术运算符是MySQL中最常用的一类运算符。MySQL支持的算术运算符包括加、减、乘、除、求余。如表7.1所示为算术运算符的符号、作用、表达式的形式。
表7.1 算术运算符
加(+)、减(-)和乘(*)可以同时运算多个操作数。除号(/)和求余运算符(%)也可以同时计算多个操作数,但是这两个符号计算多个操作数不太好。DIV和MOD这两个运算符只有两个参数。进行除法和求余的运算时,如果x2参数是0时,计算结果将是空值(NULL)。
例7.1使用算术运算符对数据表tb_book1中的row字段值进行加、减、乘、除运算,计算结果如图7.1所示。(实例位置:光盘\TM\sl\7\7.1)

图7.1 使用算术运算符计算数据
结果输出了row字段的原值,以及执行算术运算符后得到的值。
6****.1.2 比较运算符
比较运算符是查询数据时最常用的一类运算符。SELECT语句中的条件语句经常要使用比较运算符。通过这些比较运算符,可以判断表中的哪些记录是符合条件的。比较运算符的符号、名称和应用示例如表7.2所示。
表7.2 比较运算符
下面对几种较常用的比较运算符进行详解。
1.运算符“=”
“=”用来判断数字、字符串和表达式等是否相等。如果相等,返回1,否则返回0。
在运用运算符“=”判断两个字符是否相同时,数据库系统都是根据字符的ASCII码进行判断的。如果ASCII码相等,则表示这两个字符相同。如果ASCII码不相等,则表示两个字符不同。切记空值(NULL)不能使用“=”来判断。
例7.2运用运算符“=”查询出id等于27的记录,查询结果如图7.2所示。(实例位置:光盘\TM\sl\7\7.2)

图7.2 使用“=”查询记录
从结果中可以看出,id等于27的记录返回值为1,id不等于27的记录,返回值则为0。
2.运算符“<>”和“!=”
“<>”和“!=”用来判断数字、字符串、表达式等是否不相等。如果不相等,则返回1;否则,返回0。这两个符号也不能用来判断空值(NULL)。
例7.3运用运算符“<>”和“!=”判断数据表tb_book中的row字段值是否等于1、41或24。运算结果如图7.3所示。(实例位置:光盘\TM\sl\7\7.3)

图7.3 使用运算符“<>”和“!=”判断数据
结果显示返回值都为1,这表示记录中的row字段值不等于1、41、24。
3.运算符“>”
“>”用来判断左边的操作数是否大于右边的操作数。如果大于,返回1;否则,返回0。同样空值(NULL)不能使用“>”来判断。
例7.4使用运算符“>”来判断数据表tb_book中的row字段值是否大于90,是则返回1,否则返回0,空值返回NULL。运算结果如图7.4所示。(实例位置:光盘\TM\sl\7\7.4)

图7.4 使用运算符“>”查询数据
运算符“<”、运算符“<=”和运算符“>=”都与运算符“>”如出一辙,其使用方法基本相同,这里不再赘述。
4.运算符IS NULL
IS NULL用来判断操作数是否为空值(NULL)。操作数为NULL时,结果返回1;否则,返回0。IS NOT NULL刚好与IS NULL相反。
例7.5用运算符IS NULL来判断数据表tb_book中的row字段值是否为空值,查询结果如图7.5所示。(实例位置:光盘\TM\sl\7\7.5)

图7.5 使用运算符IS NULL来判断字段值是否为空
结果显示,row字段值为空的返回值为1,不为空的返回值为0。
“=”“<>”“!=”“>”“>=”“<”“<=”等运算符都不能用来判断空值(NULL)。一旦使用,结果将返回NULL。如果要判断一个值是否为空值,可以使用“<=>”、IS NULL和IS NOT NULL来判断。注意:NULL和' NULL'是不同的,前者表示为空值,后者表示一个由4个字母组成的字符串。
5.运算符BETWEEN AND
BETWEEN AND用于判断数据是否在某个取值范围内,其表达式如下。
x1 BETWEEN m AND n
如果x1大于等于m,且小于等于n,结果将返回1,否则将返回0。
例7.6运用运算符BETWEEN AND判断数据表tb_book中的row字段值是否在1050及2528之间,查询结果如图7.6所示。(实例位置:光盘\TM\sl\7\7.6)

图7.6 使用运算符BETWEEN AND判断row字段值的范围
从查询结果中可以看出,在范围内则返回1,否则返回0,空值返回NULL。
6.运算符IN
IN用于判断数据是否存在于某个集合中,其表达式如下。
x1 IN(值1,值2,…,值n)
如果x1等于值1到值n中的任何一个值,结果将返回1;如果不是,结果将返回0。
例7.7下面运用运算符IN判断数据表tb_book中的row字段值是否在指定的范围内,查询结果如图7.7所示。(实例位置:光盘\TM\sl\7\7.7)

图7.7 使用运算符IN判断row字段值的范围
查询结果如图7.7所示,在范围内则返回1,否则返回0,空值返回NULL。
7.运算符LIKE
LIKE用来匹配字符串,其表达式如下。
x1 LIKE s1
如果x1与字符串s1匹配,结果将返回1;否则返回0。
例7.8使用运算符LIKE判断数据表tb_book中的user字段值是否与指定的字符串匹配,查询结果如图7.8所示。(实例位置:光盘\TM\sl\7\7.8)

图7.8 使用运算符LIKE判断user字段是否匹配某字符
查询结果如图7.8所示,user字段值为mr字符的记录,结果则返回1,否则返回0;user字段值中包含l字符的记录,匹配则返回1,否则返回0。
8.运算符REGEXP
REGEXP同样用于匹配字符串,但其使用的是正则表达式进行匹配,其表达式如下。
x1 REGEXP '匹配方式'
如果x1满足匹配方式,结果将返回1;否则将返回0。
例7.9使用运算符REGEXP来匹配user字段的值是否以指定字符开头、结尾,同时是否包含指定的字符串,执行结果如图7.9所示。(实例位置:光盘\TM\sl\7\7.9)

图7.9 使用REGEXP运算符匹配字符串
本例使用运算符REGEXP判断数据表tb_book表中的user字段值是否以m字符开头;是否以g字符结尾;在user字段值中是否包含m字符,如果满足条件则返回1,否则返回0。
使用运算符REGEXP匹配字符串,其使用方法非常简单。REGEXP运算符经常与“”“$”和“.”一起使用。“”用来匹配字符串的开始部分;“$”用来匹配字符串的结尾部分;“.”用来代表字符串中的一个字符。
6****.1.3 逻辑运算符
逻辑运算符用来判断表达式的真假。如果表达式是真,结果返回1;如果表达式是假,结果返回0。逻辑运算符又称为布尔运算符。MySQL中支持4种逻辑运算符,分别是与、或、非和异或。如表7.3所示为4种逻辑运算符的符号及作用。
表7.3 逻辑运算符
1.与运算
“&&”或者AND是与运算的两种表达方式。如果所有数据不为0且不为空值(NULL)时,结果返回1;如果存在任何一个数据为0时,结果返回0;如果存在一个数据为NULL且没有数据为0时,结果返回NULL。与运算符支持多个数据同时进行运算。
例7.10运用运算符“&&”判断row字段的值是否存在0或者NULL(“row&&1”(row字段值与1)和“row&&0”(row字段值与0)),如果存在则返回1,否则返回0,空值返回NULL。执行结果如图7.10所示。(实例位置:光盘\TM\sl\7\7.10)

图7.10 使用运算符&&判断数据
2.或运算
“||”或者OR表示或运算。所有数据中存在任何一个数据为非0的数字时,结果返回1;如果数据中不包含非0的数字,但包含NULL时,结果返回NULL;如果操作数中有0时,结果返回0。或运算符“||”也可以同时操作多个数据。
例7.11运用运算符OR判断数据表tb_book中row字段是否包含NULL或者非0数字(“row OR 1”和“row OR 0”)。执行结果如图7.11所示。(实例位置:光盘\TM\sl\7\7.11)

图7.11 使用运算符OR匹配数据
结果显示,“row OR 1”中包含NULL和1这个非0的数字,所以返回结果为1;“row OR 0”中包含非0的数字、NULL和0的数字,所以返回NULL和1。
3.非运算
“!”或者NOT表示非运算。通过非运算,将返回与操作数据相反的结果。如果操作数据是非0的数字,结果返回0;如果操作数据是0,结果返回1;如果操作数据是NULL,结果返回NULL。
例7.12运用运算符“!”判断tb_book表中row字段的值是否为0或者NULL。执行结果如图7.12所示。(实例位置:光盘\TM\sl\7\7.12)

图7.12 使用运算符“!”判断数据
结果显示,row字段中值为NULL的记录,返回值为NULL,不为0的记录,返回值为0。
4.异或运算
XOR表示异或运算。只要其中任何一个操作数据为NULL时,结果返回NULL;如果两个操作数都是非0值,或者都是0,则返回结果为0;如果一个为0,另一个为非0值,返回结果是1。
例7.13使用运算符XOR判断数据表tb_book中row字段值是否为NULL(“row XOR 1”和“row XOR 0”)。执行结果如图7.13所示。(实例位置:光盘\TM\sl\7\7.13)

图7.13 使用运算符XOR判断数据
结果显示,“row XOR 1”中row字段中的值为非0数字和NULL值,所以返回值为0和NULL;“row XOR 0”中包含0,所以返回值为1;而row字段值为NULL的记录,返回值则为NULL。
6****.1.4 位运算符
位运算符是在二进制数上进行计算的运算符。位运算会先将操作数变成二进制数再进行位运算,然后再将计算结果从二进制数变回十进制数。MySQL中支持6种位运算符,分别是按位与、按位或、按位取反、按位异或、按位左移和按位右移。6种位运算符的符号及作用如表7.4所示。
表7.4 位运算符
例7.14将数字4和6进行按位与、按位或,并将4按位取反。执行结果如图7.14所示。(实例位置:光盘\TM\sl\7\7.14)

图7.14 位运算的实例
6****.1.5 运算符的优先级
由于在实际应用中可能需要同时使用多个运算符。这就必须考虑运算符的运算顺序。正所谓:闻道有先后,术业有专攻。
本节将具体阐述MySQL运算符使用的优先级,如表7.5所示。按照从高到低,从左到右的级别进行运算操作。如果优先级相同,则表达式左边的运算符先运算。
表7.5 MySQL运算符的优先级
6****.2 流程控制语句
在MySQL中,常见的过程式SQL语句可以用在一个存储过程体中。其中包括IF语句、CASE语句、LOOP语句、WHILE语句、ITERATE语句和LEAVE语句,它们可以进行流程控制。
6****.2.1 IF语句
IF语句用来进行条件判断,根据不同的条件执行不同的操作。该语句在执行时首先判断IF后的条件是否为真,则执行THEN后的语句,如果为假则继续判断IF语句直到为真为止,当以上都不满足时则执行ELSE语句后的内容。IF语句表示形式如下。
IF condition THEN
…
[ELSE condition THEN]
…
[ELSE]
…
ENDIF
例7.15下面通过if…then…else结构首先判断传入参数的值是否为1,如果是则输出1,如果不是则再判断该传入参数的值是否为2,如果是则输出2,当以上条件都不满足时输出3。其代码如下。(实例位置:光盘\TM\sl\7\7.15)
delimiter //
create procedure example_if(in x int)
begin
if x=1 then
select 1;
elseif x=2 then
select 2;
else
select 3;
end if;
end
//
以上代码的运行结果如图7.15所示。

图7.15 应用IF语句的存储过程
通过MySQL调用该存储过程。其运行结果如图7.16所示。

图7.16 调用example_if()存储过程
6****.2.2 CASE语句
CASE语句为多分支语句结构,该语句首先从WHEN后的VALUE中查找与CASE后的VALUE相等的值,如果查找到则执行该分支的内容,否则执行ELSE后的内容。CASE语句表示形式如下。
CASE value
WHEN value THEN …
[WHEN valueTHEN…]
[ELSE…]
END CASE
其中,value参数表示条件判断的变量;WHEN...THEN中的value参数表示变量的取值。
CASE语句另一种语法表示形式如下。
CASE WHEN value THEN…
[WHEN valueTHEN…]
[ELSE…]
END CASE
例7.16下面通过CASE语句首先判断传入参数的值是否为1,如果条件成立则输出1,如果条件不成立则再判断该传入参数的值是否为2,如果成立则输出2,当以上条件都不满足时输出3。代码如下。(实例位置:光盘\TM\sl\7\7.16)
delimiter //
create procedure example_case(in x int)
begin
case x
when 1 then select 1;
when 2 then select 2;
else select 3;
end case;
end
//
运行该示例的结果如图7.17所示。

图7.17 应用CASE语句的存储过程
调用该存储过程,其运行结果如图7.18所示。

图7.18 调用example_case()存储过程
6****.2.3 WHILE循环语句
WHILE循环语句执行时首先判断condition条件是否为真,如果是则执行循环体,否则退出循环。该语句表示形式如下。
WHILE condition DO
…
END WHILE;
例7.17下面应用WHILE语句求前100项的和。首先定义变量i和s,分别用来控制循环的次数和保存前100项和,当变量i的值小于或等于100时,使s的值加i,并同时使i的值增1。直到i大于100时退出循环并输出结果。其代码如下所示。(实例位置:光盘\TM\sl\7\7.17)
delimiter //
create procedure example_while (out sum int)
begin
declare i int default 1;·
declare s int default 0;
while i<=100 do
set s=s+i;
set i=i+1;
end while;
set sum=s;
end
//
运行以上代码的结果如图7.19所示。

图7.19 应用WHILE语句的存储过程
调用该存储过程,调用语句如下所示。
call example_while(@s)
mysql>select @s
调用该存储过程的结果如图7.20所示。

图7.20 调用example_while()存储过程
6****.2.4 LOOP循环语句
该循环没有内置的循环条件,但可以通过LEAVE语句退出循环。LOOP语句表示形式如下。
LOOP
…
END LOOP
LOOP允许某特定语句或语句群的重复执行,实现一个简单的循环构造,中间省略的部分是需要重复执行的语句。在循环内的语句一直重复直至循环被退出,退出循环应用LEAVE语句。
LEAVE语句经常和BEGIN...END或循环一起使用,其表示形式如下。
LEAVE label
label是语句中标注的名字,这个名字是自定义的。加上LEAVE关键字就可以用来退出被标注的循环语句。
例7.18下面应用LOOP语句求前100项的和。首先定义变量i和s,分别用来控制循环的次数和保存前100项的和,进入该循环体后首先使s的值加i,之后使i加1并进入下次循环,直到i大于100,通过LEAVE语句退出循环并输出结果。其代码如下。(实例位置:光盘\TM\sl\7\7.18)
delimiter //
create procedure example_loop (out sum int)
begin
declare i int default 1;
declare s int default 0;
loop_label:loop
set s=s+i;
set i=i+1;
if i>100 then
leave loop_label;
end if;
end loop;
set sum=s;
end
//
上述代码的运行结果如图7.21所示。

图7.21 应用LOOP语句创建存储过程
调用名称为example_loop的存储过程,其代码如下。
call example_loop(@s)
select @s
运行结果如图7.22所示。

图7.22 调用example_loop()存储过程
6****.2.5 REPEAT循环语句
该语句先执行一次循环体,之后判断condition条件是否为真,为真则退出循环,否则继续执行循环。REPEAT语句表示形式如下。
REPEAT
…
UNTIL condition
END REPEAT
例7.19下面应用REPEAT语句求前100项和。首先定义变量i和s,分别用来控制循环的次数和保存前100项和,进入循环体后首先使s的值加i,之后使i的值加1,直到i大于100时退出循环并输出结果。(实例位置:光盘\TM\sl\7\7.19)
delimiter //
create procedure example_repeat (out sum int)
begin
declare i int default 1;
declare s int default 0;
repeat
set s=s+i;
set i=i+1;
until i>100
end repeat;
set sum=s;
end
//
以上代码的运行结果如图7.23所示。

图7.23 应用REPEAT语句创建存储过程
调用该存储过程,相关代码如下所示。
call example_repeat(@s)
select @s
调用该存储过程的运行结果如图7.24所示。

图7.24 调用example_repeat()存储过程
循环语句中还有一个ITERATE语句,它可以出现在LOOP、REPEAT和WHILE语句内,其意为“再次循环”。该语句格式如下。
ITERATE label
该语句的格式与LEAVE大同小异,区别在于:LEAVE语句是离开一个循环,而ITERATE语句是重新开始一个循环。
与一般程序设计流程控制不同的是:存储过程并不支持FOR循环。
6****.3 小结
本章对MySQL的运算符和流程控制语句进行了详细讲解,并通过举例说明,帮助读者更好地理解所学知识的用法。在阅读本章时,读者应该重点掌握各种运算符和流程控制语句的使用,其中,位运算符是本章的难点。因为,位运算符需要将操作数转换为二进制数,然后进行位运算。这要求读者能够掌握二进制运算的相关知识。
6****.4 实践与练习
1.编写SQL语句,将数字2、0和null之间的任意两个进行逻辑运算。(答案位置:光盘\TM\sl\7\7.20)
2.应用WHILE语句求前10项的和。(答案位置:光盘\TM\sl\7\7.21)
第7章 表数据的增、删、改
功创建数据库和数据表以后,就可以针对表中的数据进行各种交互操作了。这些操作可以有效地使用、维护和管理数据库中的表数据,其中最常用的就是添加、修改和删除操作了。本章将详细介绍如何通过SQL语句来实现表数据的增、删和改操作。
通过阅读本章,读者可以:
l 掌握插入表数据的方法
l 掌握修改表中数据的方法
l 掌握删除表数据的两种方法
7****.1 插入数据
在建立一个空的数据库和数据表时,首先需要考虑的是如何向数据表中添加数据,该操作可以使用INSERT语句来完成。使用INSERT语句可以向一个已有数据表中插一个新行,也就是插入一行新记录。在MySQL中,INSERT语句有3种语法格式,分别是INSERT...VALUES语句、INSERT...SET语句和INSERT...SELECT语句。下面将分别进行介绍。
7****.1.1 使用INSERT...VALUES语句插入数据
使用INSERT...VALUES语句插入数据,是INSERT语句的最常用的语法格式。它的语法格式如下。
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] 数据表名 [(字段名,...)]
VALUES ({值 | DEFAULT},...),(...),...
[ ON DUPLICATE KEY UPDATE 字段名=表达式,...]
参数说明如下。
(1)[LOW_PRIORITY|DELAYED|HIGH_PRIORITY]:可选项,其中,LOW_PRIORITY是INSERT、UPDATE和DELETE语句都支持的一种可选修饰符,通常应用在多用户访问数据库的情况下,用于指示MySQL降低INSERT、DELETE或UPDATE操作执行的优先级;DELAYED是INSERT语句支持的一种可选修饰符,用于指定MySQL服务器把待插入的行数据放到一个缓冲器中,直到待插数据的表空闲时,才真正在表中插入数据行;HIGH_PRIORITY是INSERT和SELECT语句支持的一种可选修饰符,用于指定INSERT和SELECT操作优先执行的。
(2)[IGNORE]:可选项,表示在执行INSERT语句时,所出现的错误都会被当作警告处理。
(3)[INTO]数据表名:可选项,用于指定被操作的数据表。
(4)[(字段名,...)]:可选项,当不指定该选项时,表示要向表中所有列插入数据,否则表示向数据表的指定列插入数据。
(5)VALUES ({值| DEFAULT},...),(...),...:必选项,用于指定需要插入的数据清单,其顺序必须与字段的顺序相对应。其中的每一列的数据可以是一个常量、变量、表达式或者NULL,但是其数据类型要与对应的字段类型相匹配;也可以直接使用DEFAULT关键字,表示为该列插入默认值,但是使用的前提是已经明确指定了默认值,否则会出错。
(6)ON DUPLICATE KEY UPDATE子句:可选项,用于指定向表中插入行时,如果导致UNIQUE KEY或PRIMARY KEY出现重复值,系统会根据UPDATE后的语句修改表中原有行数据。
INSERT...VALUES语句在使用时,通常有以下3种方式。
1.插入完整数据
例8.1通过INSERT...VALUES语句向数据表tb_admin中插入一条完整的数据。(实例位置:光盘\TM\sl\8\8.1)
(1)在编写SQL语句之前,先查看一下数据表tb_admin的表结构,具体代码如下。
DESC db_database08.tb_admin;
运行效果如图8.1所示。

图8.1 查看数据表tb_admin的表结构
(2)编写SQL语句,先选择数据表所在的数据库,然后再应用INSERT...VALUES语句实现向数据表tb_admin中插入一条完整的数据,具体代码如下。
USE db_database08;
INSERT INTO tb_admin VALUES(1,'mr','mrsoft','2014-09-05 10:25:20');
运行效果如图8.2所示。

图8.2 向数据表tb_admin中插入一条完整的数据
(3)通过SELECT * FROM tb_admin语句来查看数据表tb_admin中的数据,具体代码如下。
SELECT * FROM tb_admin;
执行效果如图8.3所示。

图8.3 查看新插入的数据
2.插入数据记录的一部分
通过INSERT...VALUES语句还可以实现向数据表中插入数据记录的一部分,也就是只插入表的一行中的某几个字段的值,下面通过一个具体的实例来演示如何向数据表中插入数据记录的一部分。还是以例8.1中使用的数据表tb_admin为例进行插入。
例8.2通过INSERT...VALUES语句向数据表tb_admin中插入数据记录的一部分。(实例位置:光盘\TM\sl\8\8.2)
(1)编写SQL语句,先选择数据表所在的数据库,然后再应用INSERT...VALUES语句实现向数据表tb_admin中插入一条记录,只包括user和password字段的值,具体代码如下。
USE db_database08;
INSERT INTO tb_admin (user,password)
VALUES('rjkflm','111');
运行效果如图8.4所示。

图8.4 向数据表tb_admin中插入数据记录的一部分
(2)通过SELECT * FROM tb_admin语句来查看数据表tb_admin中的数据,具体代码如下。
SELECT * FROM tb_admin;
执行效果如图8.5所示。

图8.5 查看新插入的数据
由于在设计数据表时,将id字段设置为自动编号,所以即使没有指定id的值,MySQL也会自动为它填上相应的编号。
3.插入多条记录
通过INSERT...VALUES语句还可以实现一次性插入多条数据记录。使用该方法批量插入数据,比使用多条单行的INSERT语句的效率要高。下面将通过一个具体的实例演示如何一次插入多条记录。
例8.3通过INSERT...VALUES语句向数据表tb_admin中一次插入多条记录。(实例位置:光盘\TM\sl\8\8.3)
(1)编写SQL语句,先选择数据表所在的数据库,然后再应用INSERT...VALUES语句实现向数据表tb_admin中插入3条记录,都只包括user、password和createtime字段的值,具体代码如下。
USE db_database08;
INSERT INTO tb_admin (user,password,createtime)
VALUES('mrbccd','111', '2014-09-05 10:35:26')
,( 'mingri','111', '2014-09-05 10:45:27')
,( 'mingrisoft','111', '2014-09-05 10:55:28');
运行效果如图8.6所示。

图8.6 向数据表tb_admin中插入3条记录
(2)通过SELECT * FROM tb_admin语句来查看数据表tb_admin中的数据,具体代码如下。
SELECT * FROM tb_admin;
执行效果如图8.7所示。

图8.7 查看新插入的3行数据
7****.1.2 使用INSERT...SET语句插入数据
在MySQL中,除了可以使用INSERT...VALUES语句插入数据外,还可以使用INSERT...SET语句插入数据。这种语法格式用于通过直接给表中的某些字段指定对应的值来实现插入指定数据,对于未指定值的字段将采用默认值进行添加。INSERT...SET语句的语法格式如下。
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] 数据表名
SET 字段名={值 | DEFAULT}, ...
[ ON DUPLICATE KEY UPDATE 字段名=表达式,...]
参数说明如下。
(1)[LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]:可选项,其作用与INSERT...VALUES语句相同,这里将不再赘述。
(2)[INTO]数据表名:用于指定被操作的数据表,其中,[INTO]为可选项,可以省略。
(3)SET字段名={值| DEFAULT}:用于给数据表中的某些字段设置要插入的值。
(4)ON DUPLICATE KEY UPDATE子句:可选项,其作用与INSERT...VALUES语句相同,这里将不再赘述。
例8.4通过INSERT...SET语句向数据表tb_admin中插入一条记录。(实例位置:光盘\TM\sl\8\8.4)
(1)编写SQL语句,先选择数据表所在的数据库,然后再应用INSERT...SET语句实现向数据表tb_admin中插入一条记录,包括user、password和createtime字段的值,具体代码如下。
USE db_database08;
INSERT INTO tb_admin
SET user='mrbccd',password='111',createtime= '2014-09-06 10:35:26';
运行效果如图8.8所示。

图8.8 向数据表tb_admin中插入一条记录
(2)通过SELECT * FROM tb_admin语句来查看数据表tb_admin中的数据,具体代码如下。
SELECT * FROM tb_admin;
执行效果如图8.9所示。

图8.9 查看新插入的一行数据
7****.1.3 插入查询结果
在MySQL中,支持将查询结果插入到指定的数据表中,这可以通过INSERT...SELECT语句来实现。
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] 数据表名 [(字段名,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE 字段名=表达式, ... ]
参数说明如下。
(1)[LOW_PRIORITY|DELAYED|HIGH_PRIORITY] [IGNORE]:可选项,其作用与INSERT...VALUES语句相同,这里不再赘述。
(2)[INTO]数据表名:用于指定被操作的数据表,其中,[INTO]为可选项,可以省略。
(3)[(字段名,...)]:可选项,当不指定该选项时,表示要向表中所有列插入数据,否则表示向数据表的指定列插入数据。
(4)SELECT子句:用于快速地从一个或者多个表中取出数据,并将这些数据作为行数据插入到目标数据表中。需要注意的是,SELECT子句返回的结果集中的字段数、字段类型必须与目标数据表完全一致。
(5)ON DUPLICATE KEY UPDATE子句:可选项,其作用与INSERT...VALUES语句相同,这里不再赘述。
例8.5从数据表tb_mrbook中查询出user和pass字段的值,插入到数据表tb_admin中。(实例位置:光盘\TM\sl\8\8.5)
(1)查看数据表tb_mrbook的表结构,具体代码如下。
DESC db_database08.tb_mrbook;
执行效果如图8.10所示。

图8.10 数据表tb_mrbook的表结构
(2)查询数据表tb_mrbook中的数据,具体代码如下。
SELECT * FROM tb_mrbook;
执行效果如图8.11所示。

图8.11 数据表tb_mrbook的数据
(3)编写SQL语句,实现从数据表tb_mrbook中查询user和pass字段的值,插入到数据表tb_admin中,具体代码如下。
INSERT INTO db_database08.tb_admin
(user,password)
SELECT user,pass FROM tb_mrbook;
执行效果如图8.12所示。

图8.12 将查询结果插入到数据表
(4)通过SELECT语句来查看数据表tb_admin中的数据,具体代码如下。
SELECT * FROM db_database08.tb_admin;
执行效果如图8.13所示。

图8.13 查看新插入的数据
7****.2 修改数据
要执行修改的操作可以使用UPDATE语句,语法如下。
UPDATE [LOW_PRIORITY] [IGNORE] 数据表名
SET 字段1=值1 [, 字段2=值2...]
[WHERE 条件表达式]
[ORDER BY...]
[LIMIT 行数]
参数说明如下。
(1)[LOW_PRIORITY]:可选项,表示在多用户访问数据库的情况下可用于延迟UPDATE操作,直到没有别的用户再从表中读取数据为止。这个过程仅适用于表级锁的存储引擎(如IyISAM、MEMORY和MERGE)。
(2)[IGNORE]:在MySQL中,通过UPDATE语句更新表中多行数据时,如果出现错误,那么整个UPDATE语句操作都会被取消,错误发生前更新的所有行将被恢复到它们原来的值。因此,为了在发生错误时也要继续进行更新,则可以在UPDATE语句中使用IGNORE关键字。
(3)SET子句:必选项,用于指定表中要修改的字段名及其字段值。其中的值可以是表达式,也可以是该字段所对应的默认值。如果指定默认值,那么使用关键字DEFAULT指定。
(4)WHERE子句:可选项,用于限定表中要修改的行,如果不指定该子句,那么UPDATE语句会更新表中的所有行。
(5)ORDER BY子句:可选项,用于限定表中的行被修改的次序。
(6)LIMIT子句:可选项,用于限定被修改的行数。
例8.6将管理员信息表tb_admin中用户名为mrbccd的管理员密码111修改为123。(实例位置:光盘\TM\sl\8\8.6)
编写SQL语句修改用户名为mrbccd的管理员密码为123,具体代码如下。
UPDATE db_database08.tb_admin SET password='123' WHERE user='mrbccd';
运行效果如图8.14所示。

图8.14 将mrbccd用户的密码更改为123
更新时一定要保证WHERE子句的正确性,一旦WHERE子句出错,将会破坏所有改变的数据。
查询修改后的数据库内容,代码如下。
SELECT * FROM db_database08.tb_admin WHERE user='mrbccd';
执行结果如图8.15所示。

图8.15 查看修改后的结果
7****.3 删除数据
在数据库中,有些数据已经失去意义或者错误时就需要将它们删除,在MySQL中,可以使用DELETE语句或者TRUNCATE TABLE语句删除表中的一行或多行数据,下面分别进行介绍。
7****.3.1 通过DELETE语句删除数据
通过DELETE语句删除数据的基本语法格式如下。
DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM 数据表名
[WHERE 条件表达式]
[ORDER BY...]
[LIMIT 行数]
参数说明如下。
(1)[LOW_PRIORITY]:可选项,表示在多用户访问数据库的情况下可用于延迟DELETE操作,直到没有别的用户再从表中读取数据为止。这个过程仅适用于表级锁的存储引擎(如IyISAM、MEMORY和MERGE)。
(2)[QUICK]:可选项,用于加快部分种类的删除操作的速度。
(3)[IGNORE]:在MySQL中,通过DELETE语句删除表中多行数据时,如果出现错误,那么整个DELETE语句操作都会被取消,错误发生前更新的所有行将被恢复到它们原来的值。因此,为了在发生错误时也要继续进行删除,则可以在DELETE语句中使用IGNORE关键字。
(4)数据表名:用于指定要删除的数据表的表名。
(5)WHERE子句:可选项,用于限定表中要删除的行,如果不指定该子句,那么DELETE语句会删除表中的所有行。
(6)ORDER BY子句:可选项,用于限定表中的行被删除的次序。
(7)LIMIT子句:可选项,用于限定被删除的行数。
该语句在执行过程中,如果没有指定WHERE条件,将删除所有的记录;如果指定了WHERE条件,将按照指定的条件进行删除。
例8.7删除管理员数据表tb_admin中用户名为mr的记录信息。(实例位置:光盘\TM\sl\8\8.7)
(1)编写SQL语句除管理员数据表tb_admin中用户名为mr的记录信息,具体代码如下。
USE db_database08;
DELETE FROM tb_admin WHERE user='mr';
运行效果如图8.16所示。

图8.16 删除数据表中用户名为mr的记录
(2)通过SELECT语句来查看删除记录后数据表tb_admin中的数据,具体代码如下。
SELECT * FROM tb_admin;
执行结果如图8.17所示。

图8.17 查看删除后的结果
删除数据前,数据表tb_admin中的数据如图8.13所示。
在实际的应用中,执行删除操作时,执行删除的条件一般应该为数据的id,而不是具体某个字段值,这样可以避免一些错误发生。
7****.3.2 通过TRUNCATE TABLE语句删除数据
在删除数据时,如果要从表中删除所有的行,通过TRUNCATE TABLE语句删除数据的基本语法格式如下。
TRUNCATE [TABLE] 数据表名
在上面的语法中,数据表名表示的就是删除的数据表的表名,也可以使用“数据库名.数据表名”来指定该数据表隶属于哪个数据库。
由于TRUNCATE TABLE语句会删除数据表中的所有数据,并且无法恢复,因此使用TRUNCATE TABLE语句时一定要十分小心。
例8.8使用TRUNCATE TABLE语句清空管理员数据表tb_admin,具体代码如下。(实例位置:光盘\TM\sl\8\8.8)
TRUNCATE TABLE db_database08.tb_admin;
运行效果如图8.18所示。

图8.18 清空管理员数据表tb_admin
DELETE语句和TRUNCATE TABLE语句的区别如下。
(1)使用TRUNCATE TABLE语句后,表中的AUTO_INCREMENT计数器将被重新设置为该列的初始值。
(2)对于参与了索引和视图的表,不能使用TRUNCATE TABLE语句来删除数据,而应用使用DELETE语句。
(3)TRUNCATE TABLE操作与DELETE操作使用的系统和事务日志资源少。DELETE语句每删除一行都会在事务日志中添加一行记录,而TRUNCATE TABLE语句是通过释放存储表数据所用的数据页来删除数据的,因此只在事务日志中记录页的释放。
7****.4 小结
本章介绍了在MySQL中向数据表中添加数据库、修改数据和删除数据的具体方法,也就是对表数据的增、删和改操作。这3种操作在实际开发中经常应用。因此,对于本章的内容需要认真学习,争取做到举一反三、灵活应用。
7****.5 实践与练习
1.编写SQL语句,先选择数据表所在的数据库,然后再应用INSERT...VALUES语句实现向数据表tb_admin中插入一条记录,只包括user、password和createtime字段的值。(答案位置:光盘\TM\sl\8\8.9)
2.编写SQL语句,使用DELETE语句清空管理员数据表tb_admin。(答案位置:光盘\TM\sl\8\8.10)
第8章 数据查询
数据查询是指从数据库中获取所需要的数据。数据查询是数据库操作中最常用,也是最重要的操作。通过不同的查询方式可以获得不同的数据。用户可以根据自己对数据的需求使用不同的查询方式。在MySQL中是使用SELECT语句来查询数据的。本章将对查询语句的基本语法、在单表上查询数据、使用聚合函数查询数据、合并查询结果等内容进行详细的讲解,帮助读者轻松了解查询数据的语句。通过阅读本章,读者可以:
l 了解基本查询语句
l 了解单表查询的方法
l 了解用聚合函数查询的方法
l 掌握连接查询和子查询的方法
l 掌握合并查询结果的方法
l 掌握定义表和字段别名的方法
l 掌握正则表达式查询的方法
8.1 基本查询语句
SELECT语句是最常用的查询语句,它的使用方式有些复杂,但功能是相当强大的。SELECT语句的基本语法如下。
select selection_list //要查询的内容,选择哪些列
from 数据表名 //指定数据表
where primary_constraint //查询时需要满足的条件,行必须满足的条件
group by grouping_columns //如何对结果进行分组
order by sorting_cloumns //如何对结果进行排序
having secondary_constraint //查询时满足的第二条件
limit count //限定输出的查询结果
其中使用的子句将在后面逐个介绍。下面先介绍SELECT语句的简单应用。
1.使用SELECT语句查询一个数据表
使用SELECT语句时,首先要确定所要查询的列。“*”代表所有的列。例如,查询db_database09数据库user表中的所有数据,代码如下。
mysql> use db_database09
Database changed
mysql> select * from user;
+----+------+----------+--------------+
| id | user | lxdh | jtdz
+----+------+----------+--------------+
| 1 | mr | 12345678 | 长春市
| 2 | mrsoft | 87654321 | 四平市
+----+------+----------+--------------+
2 rows in set (0.00 sec)
这是查询整个表中所有列的操作,还可以针对表中的某一列或多列进行查询。
2.查询表中的一列或多列
针对表中的多列进行查询,只要在select后面指定要查询的列名即可,多列之间用“,”分隔。例如,查询user表中的id和lxdh,代码如下。
mysql> select id, lxdh from user ;
+----+----------+
| id | lxdh
+----+----------+
| 1 | 12345678
| 2 | 87654321
+----+----------+
2 rows in set (0.00 sec)
3.从一个或多个表中获取数据
使用SELECT语句进行查询,需要确定所要查询的数据在哪个表或哪些表中,在对多个表进行查询时,同样使用“,”对多个表进行分隔。
例9.1从tb_admin表和tb_students表中查询出tb_admin.id、tb_admin.tb_user、tb_students.id和tb_students.name字段的值,其代码如下。(实例位置:光盘\TM\sl\9\9.1)
mysql> select tb_admin.id,tb_admin.tb_user,tb_students.id,tb_students.name from
tb_admin,tb_students;
+----+----------+----+------+
| id | tb_user | id | name
+----+----------+----+------+
| 1 | mr | 1 | 潘攀
| 2 | 明日科技 | 1 | 潘攀
+----+----------+----+------+
2 rows in set (0.03 sec)
在查询数据库中的数据时,如果数据中涉及中文字符串,有可能在输出时会出现乱码。那么最后在执行查询操作之前,通过set names语句设置其编码格式,然后再输出中文字符串时就不会出现乱码了。如本例中所示,应用set names语句设置其编码格式为gb2312。
还可以在WHERE子句中使用连接运算来确定表之间的联系,然后根据这个条件返回查询结果。例如,从家庭收入表(jtsr)中查询出指定用户的家庭收入数据,条件是用户的ID为1,代码如下。
mysql> select jtsr from user,jtsr
-> where user.user=jtsr.user and user.id=1 ;
+------+
| jtsr
+------+
| 10000
+------+
2 rows in set (0.00 sec)
其中,user.user = jtsr.user将表user和jtsr连接起来,叫作等同连接;如果不使用user.user= jtsr.user,那么产生的结果将是两个表的笛卡尔积,叫作全连接。
8****.2 单表查询
单表查询是指从一张表中查询所需要的数据,所有查询操作都比较简单。
8****.2.1 查询所有字段
查询所有字段是指查询表中所有字段的数据。这种方式可以将表中所有字段的数据都查询出来。在MySQL中可以使用“*”代表所有的列,即可查出所有的字段,语法格式如下。
SELECT * FROM 表名;
其应用已经在9.1节基本查询语句中介绍过,这里不再赘述。
8****.2.2 查询指定字段
查询指定字段可以使用下面的语法格式。
SELECT 字段名 FROM 表名;
如果是查询多个字段,可以使用“,”对字段进行分隔。
例9.2查询db_database09数据库tb_login表中user和pwd两个字段,SELECT查询语句如下。(实例位置:光盘\TM\sl\9\9.2)
SELECT user,pwd FROM tb_login;
查询结果如图9.1所示。

图9.1 查询指定字段的数据
8****.2.3 查询指定数据
如果要从很多记录中查询出指定的记录,那么就需要一个查询的条件。设定查询条件应用的是WHERE子句。通过它可以实现很多复杂的条件查询。在使用WHERE子句时,需要使用一些比较运算符来确定查询的条件。其常用的比较运算符如表9.1所示。
表9.1 比较运算符
表9.1中列举的是WHERE子句常用的比较运算符,例中的id是记录的编号,name是表中的用户名。
例9.3应用WHERE子句查询tb_login表,条件是user(用户名)为mr,代码如下。(实例位置:光盘\TM\sl\9\9.3)
select * from tb_login where user = 'mr';
查询结果如图9.2所示。

图9.2 查询指定数据
8****.2.4 带关键字IN的查询
关键字IN可以判断某个字段的值是否在指定的集合中。如果字段的值在集合中,则满足查询条件,该记录将被查询出来;如果不在集合中,则不满足查询条件。其语法格式如下。
SELECT * FROM 表名 WHERE 条件 [NOT] IN(元素1,元素2,...,元素n);
(1)[NOT]:是可选项,加上NOT表示不在集合内满足条件;
(2)元素:表示集合中的元素,各元素之间用逗号隔开,字符型元素需要加上单引号。
例9.4应用IN关键字查询tb_login表中user字段为mr和lx的记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.4)
SELECT * FROM tb_login WHERE user IN('mr','lx');
查询结果如图9.3所示。

图9.3 使用关键字IN查询
例9.5使用关键字NOT IN查询tb_login表中user字段不为mr和lx的记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.5)
SELECT * FROM tb_login WHERE user NOT IN('mr','lx');
查询结果如图9.4所示。

图9.4 使用关键字NOT IN查询
8****.2.5 带关键字BETWEEN AND的范围查询
关键字BETWEEN AND可以判断某个字段的值是否在指定的范围内。如果字段的值在指定范围内,则满足查询条件,该记录将被查询出来。如果不在指定范围内,则不满足查询条件。其语法如下。
SELECT * FROM 表名 WHERE 条件 [NOT] BETWEEN 取值1 AND 取值2;
(1)[NOT]:可选项,表示不在指定范围内满足条件。
(2)取值1:表示范围的起始值。
(3)取值2:表示范围的终止值。
例9.6查询tb_login表中id值在5~7之间的数据,查询语句如下。(实例位置:光盘\TM\sl\9\9.6)
SELECT * FROM tb_login WHERE id BETWEEN 5 AND 7;
查询结果如图9.5所示。

图9.5 使用关键字BETWEEN AND查询
如果要查询tb_login表中id值不在5~7之间的数据,则可以通过NOT BETWEEN AND来完成。其查询语句如下。
SELECT * FROM tb_login WHERE id NOT BETWEEN 5 AND 7;
8****.2.6 带LIKE的字符匹配查询
LIKE属于较常用的比较运算符,通过它可以实现模糊查询。它有两种通配符:“%”和下划线“_”。
(1)“%”可以匹配一个或多个字符,可以代表任意长度的字符串,长度可以为0。例如,“明%技”表示以“明”开头,以“技”结尾的任意长度的字符串。该字符串可以代表“明日科技”“明日编程科技”“明日图书科技”等字符串。
(2)“”只匹配一个字符。例如,m_n表示以m开头,以n结尾的3个字符。中间的“”可以代表任意一个字符。
字符串“p”和“入”都算作一个字符,在这点上英文字母和中文是没有区别的。
例9.7查询tb_login表中user字段中包含mr字符的数据,查询语句如下。(实例位置:光盘\TM\sl\9\9.7)
select * from tb_login where user like '%mr%';
查询结果如图9.6所示。

图9.6 模糊查询
8****.2.7 用关键字IS NULL查询空值
关键字IS NULL可以用来判断字段的值是否为空值(NULL)。如果字段的值是空值,则满足查询条件,该记录将被查询出来。如果字段的值不是空值,则不满足查询条件。其语法格式如下。
IS [NOT] NULL
其中,“NOT”是可选项,表示字段不是空值时满足条件。
例9.8下面使用关键字IS NULL查询db_database09数据库的tb_book表中name字段的值为空的记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.8)
SELECT books,row FROM tb_book WHERE row IS NULL;
查询结果如图9.7所示。

图9.7 查询数据表tb_book中row字段值为空的记录
8****.2.8 带关键字AND的多条件查询
关键字AND可以用来联合多个条件进行查询。使用关键字AND时,只有同时满足所有查询条件的记录会被查询出来。如果不满足这些查询条件的其中一个,这样的记录将被排除掉。关键字AND的语法格式如下。
select * from 数据表名 where 条件1 and 条件2 [...AND 条件表达式n];
关键字AND连接两个条件表达式,可以同时使用多个关键字AND来连接多个条件表达式。
例9.9下面查询数据表tb_login中user字段值为mr,并且section字段值为PHP的记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.9)
select * from tb_login where user='mr' and section='php';
查询结果如图9.8所示。

图9.8 使用关键字AND实现多条件查询
8****.2.9 带关键字OR的多条件查询
关键字OR也可以用来联合多个条件进行查询,但是与关键字AND不同,关键字OR只要满足查询条件中的一个,那么此记录就会被查询出来;如果不满足这些查询条件中的任何一个,这样的记录将被排除掉。关键字OR的语法格式如下。
select * from 数据表名 where 条件1 OR 条件2 [...OR 条件表达式n];
关键字OR可以用来连接两个条件表达式。而且,可以同时使用多个关键字OR连接多个条件表达式。
例9.10下面查询tb_login表中section字段的值为“PHP”或者“程序开发”的记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.10)
select * from tb_login where section='php' or section='程序开发';
查询结果如图9.9所示。

图9.9 使用关键字OR实现多条件查询
8****.2.10 用关键字DISTINCT去除结果中的重复行
使用关键字DISTINCT可以去除查询结果中的重复记录,语法格式如下。
select distinct 字段名 from 表名;
例9.11下面使用关键字DISTINCT去除tb_login表中name字段中的重复记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.11)
select distinct name from tb_login;
查询结果如图9.10所示。去除重复记录前的name字段值如图9.11所示。

图9.10 使用关键字DISTINCT去除结果中的重复行

图9.11 去除重复记录前的name字段值
8****.2.11 用关键字ORDER BY对查询结果排序
使用关键字ORDER BY可以对查询的结果进行升序(ASC)和降序(DESC)排列,在默认情况下,ORDER BY按升序输出结果。如果要按降序排列可以使用DESC来实现。语法格式如下。
ORDER BY 字段名 [ASC|DESC];
(1)ASC表示按升序进行排序。
(2)DESC表示按降序进行排序。
对含有NULL值的列进行排序时,如果是按升序排列,NULL值将出现在最前面,如果是按降序排列,NULL值将出现在最后。
例9.12查询tb_login表中的所有信息,按照id序号进行降序排列,查询语句如下。(实例位置:光盘\TM\sl\9\9.12)
select * from tb_login order by id desc;
查询结果如图9.12所示。

图9.12 按id序号进行降序排列
8****.2.12 用关键字GROUP BY分组查询
通过关键字GROUP BY可以将数据划分到不同的组中,实现对记录进行分组查询。在查询时,所查询的列必须包含在分组的列中,目的是使查询到的数据没有矛盾。
1.使用关键字GROUP BY来分组
单独使用关键字GROUP BY查询结果只显示每组的一条记录。
例9.13使用关键字GROUP BY对tb_book表中talk字段进行分组查询,查询语句如下。(实例位置:光盘\TM\sl\9\9.13)
select id,books,talk from tb_book GROUP BY talk;
查询结果如图9.13所示。

图9.13 使用关键字GROUP BY进行分组查询
为了使分组更加直观明了,下面查询数据表tb_book中的记录,查询结果如图9.14所示。

图9.14 数据表tb_book中的记录
2.关键字GROUP BY与GROUP_CONCAT()函数一起使用
使用关键字GROUP BY和GROUP_CONCAT()函数查询,可以将每个组中的所有字段值都显示出来。
例9.14下面使用关键字GROUP BY和GROUP_CONCAT()函数对tb_book表中的talk字段进行分组查询,查询语句如下。(实例位置:光盘\TM\sl\9\9.14)
select id,books,GROUP_CONCAT(talk) from tb_book GROUP BY talk;
查询结果如图9.15所示。

图9.15 使用关键字GROUP BY与GROUP_CONCAT()函数进行分组查询
3.按多个字段进行分组
使用关键字GROUP BY也可以按多个字段进行分组。
例9.15下面对数据表tb_book表中的user字段和sort字段进行分组,分组过程中,先按照talk字段进行分组。当talk字段的值相等时,再按照sort字段进行分组,查询语句如下。(实例位置:光盘\TM\sl\9\9.15)
select id,books,talk,user from tb_book GROUP BY user,talk;
查询结果如图9.16所示。

图9.16 使用关键字GROUP BY实现多个字段分组
8****.2.13 用关键字LIMIT限制查询结果的数量
查询数据时,可能会查询出很多的记录,而用户需要的记录可能只是很少的一部分,这样就需要来限制查询结果的数量。LIMIT是MySQL中的一个特殊关键字。关键字LIMIT可以对查询结果的记录条数进行限定,控制它输出的行数。下面通过具体实例来了解关键字LIMIT的使用方法。
例9.16查询数据表tb_login中,按照id编号进行升序排列,显示前3条记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.16)
select * from tb_login order by id asc limit 3;
查询结果如图9.17所示。

图9.17 使用关键字LIMIT查询指定记录数
使用关键字LIMIT还可以从查询结果的中间部分取值。首先要定义两个参数,参数1是开始读取的第一条记录的编号(在查询结果中,第一个结果的记录编号是0,而不是1);参数2是要查询记录的个数。
例9.17查询tb_login表中,按照id编号进行升序排列,从编号1开始,查询两条记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.17)
select * from tb_login where id order by id asc limit 1,2;
查询结果如图9.18所示。

图9.18 使用关键字LIMIT查询指定记录
8****.3 聚合函数查询
聚合函数的最大特点是它们根据一组数据求出一个值。聚合函数的结果值只根据选定行中非NULL的值进行计算,NULL值被忽略。
8****.3.1 COUNT()函数
COUNT()函数,对于除“”以外的任何参数,返回所选择集合中非NULL值的行的数目;对于参数“”,返回选择集合中所有行的数目,包含NULL值的行。没有WHERE子句的COUNT(*)是经过内部优化的,能够快速返回表中所有的记录总数。
例9.18下面使用count()函数统计数据表tb_login中的记录数,查询语句如下。(实例位置:光盘\TM\sl\9\9.18)
select count(*) from tb_login;
查询结果如图9.19所示。结果显示,数据表tb_login中共有4条记录。

图9.19 使用count()函数统计记录数
8****.3.2 SUM()函数
SUM()函数可以求出表中某个字段取值的总和。
例9.19使用SUM()函数统计数据表tb_book中图书的访问量字段(row)的总和。在查询前,先来查询一下tb_book表中row字段的值,结果如图9.20所示。(实例位置:光盘\TM\sl\9\9.19)

图9.20 tb_book表中row字段的值
下面使用SUM()函数来查询,查询语句如下。
select sum(row) from tb_book;
查询结果如图9.21所示,显示row字段的总和为116。

图9.21 使用SUM()函数查询row字段值的总和
8****.3.3 AVG()函数
AVG()函数可以求出表中某个字段取值的平均值。
例9.20下面使用AVG()函数求数据表tb_book中row字段值的平均值,查询语句如下。(实例位置:光盘\TM\sl\9\9.20)
select AVG(row) from tb_book;
查询结果如图9.22所示。

图9.22 使用AVG()函数求row字段值的平均值
8****.3.4 MAX()函数
MAX()函数可以求出表中某个字段取值的最大值。
例9.21下面使用MAX()函数查询数据表tb_book中row字段的最大值,查询语句如下。(实例位置:光盘\TM\sl\9\9.21)
select MAX(row) from tb_book;
查询结果如图9.23所示。

图9.23 使用MAX()函数求row字段的最大值
下面来看一下数据表tb_book中row字段的所有值,查询结果如图9.24所示。结果显示row字段中最大值为95,与使用MAX函数查询的结果一致。

图9.24 数据表tb_book中row字段的所有值
8****.3.5 MIN()函数
MIN()函数可以求出表中某个字段取值的最小值。
例9.22使用MIN()函数查询数据表tb_book中row字段的最小值,查询语句如下。(实例位置:光盘\TM\sl\9\9.22)
select MIN(row) from tb_book;
查询结果如图9.25所示。

图9.25 使用MIN()函数求row字段的最小值
8****.4 连接查询
连接是把不同表的记录连到一起的最普遍的方法。一种错误的观念认为由于MySQL的简单性和源代码开放性,使它不擅长连接。MySQL从一开始就能够很好地支持连接,现在还以支持标准的SQL92连接语句而自豪,这种连接语句可以以多种高级方法来组合表记录。
8****.4.1 内连接查询
内连接是最普遍的连接类型,而且是最匀称的,因为它们要求构成连接的每一部分的每个表的匹配,不匹配的行将被排除。
内连接的最常见的例子是相等连接,也就是连接后的表中的某个字段与每个表中的都相同。这种情况下,最后的结果集只包含参加连接的表中与指定字段相符的行。
例9.23下面有两个表,tb_login用户信息表和tb_book图书信息表,先来分别看下各表的数据,图9.26为tb_login表的数据,图9.27为tb_book表的数据。(实例位置:光盘\TM\sl\9\9.23)

图9.26 数据表tb_login

图9.27 数据表tb_book
从上面的查询结果中可以看出,在两个表中存在一个连接——user字段,它在两个表中是等同的,tb_login表的user字段与tb_book表的user字段相等,因此可以创建两个表的一个连接。查询语句如下。
select name,books from tb_login,tb_book where tb_login.user=tb_book.user;
查询结果如图9.28所示。

图9.28 内连接查询
8****.4.2 外连接查询
与内连接不同,外连接是指使用OUTER JOIN关键字将两个表连接起来。外连接生成的结果集不仅包含符合连接条件的行数据,而且还包括左表(左外连接时的表)、右表(右外连接时的表)或两边连接表(全外连接时的表)中所有的数据行。语法格式如下。
SELECT 字段名称 FROM 表名1 LEFT|RIGHT JOIN 表名2 ON 表名1.字段名1=表名2.属性名2;
外连接分为左外连接(LEFT JOIN)、右外连接(RIGHT JOIN)和全外连接3种类型。
1.左外连接
左外连接(LEFT JOIN)是指将左表中的所有数据分别与右表中的每条数据进行连接组合,返回的结果除内连接的数据外,还包括左表中不符合条件的数据,并在右表的相应列中添加NULL值。
例9.24下面使用左外连接查询tb_login表和tb_book表,通过user字段进行连接,查询语句如下。(实例位置:光盘\TM\sl\9\9.24)
select section,tb_login.user,books,row from tb_login left join tb_book on tb_login.user= tb_book.user;
查询结果如图9.29所示。

图9.29 左外连接查询
结果显示,第一条记录的books和row字段的值为空,这是因为在tb_book表中并不存在user字段为mrkj的值。
2.右外连接
右外连接(RIGHT JOIN)是指将右表中的所有数据分别与左表中的每条数据进行连接组合,返回的结果除内连接的数据外,还包括右表中不符合条件的数据,并在左表的相应列中添加NULL。
例9.25下面使用右外连接查询tb_book表和tb_login表,两表通过user字段连接,查询语句如下。(实例位置:光盘\TM\sl\9\9.25)
select section,tb_book.user,books,row from tb_book right join tb_login on tb_book.user=tb_login.user;
查询结果如图9.30所示。

图9.30 右外连接查询
8****.4.3 复合条件连接查询
在连接查询时,也可以增加其他的限制条件。通过多个条件的复合查询,可以使查询结果更加准确。
例9.26下面使用内连接查询tb_book表和tb_login表,并且tb_book表中row字段值必须大于5,查询语句如下。(实例位置:光盘\TM\sl\9\9.26)
select section,tb_book.user,books,row from tb_book,tb_login where tb_book.user= tb_login.user and row>5;
查询结果如图9.31所示。

图9.31 复合条件连接查询
8****.5 子查询
子查询就是SELECT查询是另一个查询的附属。MySQL 4.1可以嵌套多个查询,在外面一层的查询中使用里面一层查询产生的结果集。这样就不是执行两个(或者多个)独立的查询,而是执行包含一个(或者多个)子查询的单独查询。
当遇到这样的多层查询时,MySQL从最内层的查询开始,然后从它开始向外向上移动到外层(主)查询,在这个过程中每个查询产生的结果集都被赋给包围它的父查询,接着这个父查询被执行,它的结果也被指定给父查询。
除了结果集经常由包含一个或多个值的一列组成外,子查询和常规SELECT查询的执行方式一样。子查询可以用在任何可以使用表达式的地方,它必须由父查询包围,而且,如同常规的SELECT查询,它必须包含一个字段列表(这是一个单列列表)、一个具有一个或者多个表名字的FROM子句以及可选的WHERE、HAVING和GROUP BY子句。
8****.5.1 带关键字IN的子查询
只有子查询返回的结果列包含一个值时,比较运算符才适用。假如一个子查询返回的结果集是值的列表,这时比较运算符就必须用关键字IN代替。
IN运算符可以检测结果集中是否存在某个特定的值,如果检测成功就执行外部的查询。
例9.27下面查询tb_login表中的记录,但user字段值必须在tb_book表中的user字段中出现过,查询语句如下。(实例位置:光盘\TM\sl\9\9.27)
select * from tb_login where user in(select user from tb_book);
在查询前,先来分别看一下tb_login和tb_book表中的user字段值,以便进行对比,tb_login表中的user字段值如图9.32所示。tb_book表中的user字段值如图9.33所示。

图9.32 tb_login表中的user字段值

图9.33 tb_book表中的user字段值
从上面的查询结果可以看出,在tb_book表的user字段中没有出现mrkj值。下面执行带关键字IN的子查询语句,查询结果如图9.34所示。

图9.34 使用关键字IN实现子查询
查询结果只查询出了user字段值为lx和mr的记录,因为在tb_book表的user字段中没有出现mrkj的值。
关键字NOT IN的作用与关键字IN刚好相反。在本例中,如果将IN换为NOT IN,则查询结果将会只显示一条user字段值为mrkj的记录。
8****.5.2 带比较运算符的子查询
子查询可以使用比较运算符,包括=、!=、>、>=、<和<=等。比较运算符在子查询时使用非常广泛。
例9.28下面查询图书访问量为“优秀”的图书,在tb_row表中将图书访问量按访问数划分等级,如图9.35所示。(实例位置:光盘\TM\sl\9\9.28)
从结果中看出,当访问量大于等于90时即为“优秀”,下面再来查询tb_book图书信息表中row字段的值,如图9.36所示。

图9.35 查询tb_row表中的数据

图9.36 查询tb_book表中row字段的值
结果显示,第27条记录的访问量大于90。下面使用比较运算符的子查询方式来查询访问量为优秀的图书信息,查询语句如下。
select id,books,row from tb_book where row>=(select row from tb_row where id=1);
查询结果如图9.37所示。

图9.37 使用比较运算符的子查询方式来查询访问量为“优秀”的图书信息
8****.5.3 带关键字EXISTS的子查询
使用关键字EXISTS时,内层查询语句不返回查询的记录。而是返回一个真假值。如果内层查询语句查询到满足条件的记录,就返回一个真值(true),否则将返回一个假值(false)。当返回的值为true时,外层查询语句将进行查询;当返回的值为false时,外层查询语句不进行查询或者查询不出任何记录。
例9.29下面使用子查询查询tb_book表中是否存在id值为27的记录,如果存在则查询tb_row表中的记录,如果不存在则不执行外层查询,查询语句如下。(实例位置:光盘\TM\sl\9\9.29)
select * from tb_row where exists (select * from tb_book where id=27);
查询结果如图9.38所示。

图9.38 使用关键字EXISTS的子查询
因为子查询tb_book表中存在id值为27的记录,即返回值为真,外层查询接收到真值后,开始执行查询。
当关键字EXISTS与其他查询条件一起使用时,需要使用AND或者OR来连接表达式与EXISTS关键字。
例9.30如果tb_row表中存在name值为“优秀”的记录,则查询tb_book表中row字段大于等于90的记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.30)
select id,books,row from tb_book where row>=90 and exists(select * from tb_row where name='优秀');
查询结果如图9.39所示。

图9.39 使用关键字EXISTS查询tb_book表中row字段大于等于90的记录
与关键字EXISTS刚好相反,使用关键字NOT EXISTS时,当返回的值是true时,外层查询语句不执行查询;当返回值是false时,外层查询语句将执行查询。
8****.5.4 带关键字ANY的子查询
关键字ANY表示满足其中任意一个条件。使用关键字ANY时,只要满足内层查询语句返回的结果中的任意一个,就可以通过该条件来执行外层查询语句。
例9.31查询tb_book表中row字段的值小于tb_row表中row字段最小值的记录,首先查询出tb_row表中row字段的值,然后使用关键字ANY(“<ANY”表示小于所有值)判断,查询语句如下。(实例位置:光盘\TM\sl\9\9.31)
select books,row from tb_book where row<ANY(select row from tb_row);
查询结果如图9.40所示。

图9.40 使用关键字ANY实现子查询
为了使结果更加直观,下面分别查询tb_book表和tb_row表中的row字段值,查询结果如图9.41和图9.42所示。

图9.41 tb_book表中row字段的值

图9.42 tb_row表中row字段的值
结果显示,tb_row表中row字段的最小值为50,在tb_book表中row字段小于50的记录有3条,与带关键字ANY的子查询结果相同。
8****.5.5 带关键字ALL的子查询
关键字ALL表示满足所有条件。使用关键字ALL时,只有满足内层查询语句返回的所有结果,才可以执行外层查询语句。
例9.32查询tb_book表中row字段的值大于tb_row表中row字段最大值的记录,首先使用子查询,查询出tb_row表中row字段的值,然后使用ALL关键字(“>=ALL”表示大于等于所有值)判断,查询语句如下。(实例位置:光盘\TM\sl\9\9.32)
select books,row from tb_book where row>=ALL(select row from tb_row);
查询结果如图9.43所示。

图9.43 使用关键字ALL实现子查询
为了使结果更加直观,下面分别查询tb_book表和tb_row表中的row字段值,查询结果如图9.44和图9.45所示。

图9.44 tb_book表中row字段的值

图9.45 tb_row表中row字段的值
结果显示,tb_row表中row字段的最大值为90,在tb_book表中row字段大于90的只有第2条记录95,与带关键字ALL的子查询结果相同。
关键字ANY和关键字ALL的使用方式是一样的,但是这两者有很大的区别。使用关键字ANY时,只要满足内层查询语句返回的结果中的任何一个,就可以通过该条件来执行外层查询语句;而关键字ALL则需要满足内层查询语句返回的所有结果,才可以执行外层查询语句。
8****.6 合并查询结果
合并查询结果是将多个SELECT语句的查询结果合并到一起。因为某种情况下,需要将几个SELECT语句查询出来的结果合并起来显示。合并查询结果使用关键字UNION和UNION ALL。关键字UNION是将所有的查询结果合并到一起,然后去除相同记录;而关键字UNION ALL则只是简单地将结果合并到一起,下面分别介绍这两种合并方法。
1.UNION
例9.33下面查询tb_book表和tb_login表中的user字段,并使用UNION关键字合并查询结果。在执行查询操作前,先来看一下tb_book表和tb_login表中user字段的值,查询结果如图9.46和图9.47所示。(实例位置:光盘\TM\sl\9\9.33)

图9.46 tb_book表中user字段的值

图9.47 tb_login表中user字段的值
结果显示,在tb_book表中user字段的值有两种,分别为mr和lx,而tb_login表中user字段的值有三种。下面使用关键字UNION合并两个表的查询结果,查询语句如下。
select user from tb_book
UNION
select user from tb_login;
查询结果如图9.48所示。结果显示,合并后将所有结果合并到了一起,并去除了重复值。

图9.48 使用关键字UNION合并查询结果
2.UNION ALL
查询tb_book表和tb_login表中的user字段,并使用关键字UNION ALL合并查询结果,查询语句如下。
select user from tb_book
UNION ALL
select user from tb_login;
查询结果如图9.49所示。tb_book表和tb_login表的记录请参见例9.33。

图9.49 使用关键字UNION ALL合并查询结果
8****.7 定义表和字段的别名
在查询时,可以为表和字段取一个别名,这个别名可以代替其指定的表和字段。为字段和表取别名,能够使查询更加方便,而且可以使查询结果以更加合理的方式显示。
8****.7.1 为表取别名
当表的名称特别长时,在查询中直接使用表名很不方便,这时可以为表取一个贴切的别名。
例9.34下面为tb_program表取别名为p,然后查询tb_program表中talk字段值为php的记录,查询语句如下。(实例位置:光盘\TM\sl\9\9.34)
select * from tb_program p where p.talk='PHP';
tb_program p表示tb_program表的别名为p;p.talk表示tb_program表中的talk字段。查询结果如图9.50所示。

图9.50 为表取别名
8****.7.2 为字段取别名
当查询数据时,MySQL会显示每个输出列的名词。默认情况下,显示的列名是创建表时定义的列名。同样可以为这个列取一个别名。
MySQL中为字段取别名的基本形式如下。
字段名 [AS] 别名
例9.35下面为tb_login表中的section和name字段分别取别名为login_section和login_name, SQL代码如下。(实例位置:光盘\TM\sl\9\9.35)
select section AS login_section,name AS login_name from tb_login;
查询结果如图9.51所示。

图9.51 为字段取别名
8****.8 使用正则表达式查询
正则表达式是用某种模式去匹配一类字符串的一个方式。正则表达式的查询能力比通配字符的查询能力更强大,而且更加灵活。下面详细讲解如何使用正则表达式来查询。
在MySQL中,使用关键字REGEXP来匹配查询正则表达式,其基本形式如下。
字段名 REGEXP '匹配方式'
(1)字段名:表示需要查询的字段名称。
(2)匹配方式:表示以哪种方式来进行匹配查询。其支持的模式匹配字符如表9.2所示。
表9.2 正则表达式的模式字符
这里的正则表达式与Java语言、PHP语言等编程语言中的正则表达式基本一致。
8****.8.1 匹配指定字符中的任意一个
使用方括号([])可以将需要查询字符组成一个字符集。只要记录中包含方括号中的任意字符,该记录将会被查询出来。例如,通过“[abc]”可以查询包含a、b和c3个字母中任何一个的记录。
例9.36下面从info表name字段中查询包含c、e和o 3个字母中任意一个的记录。SQL代码如下。(实例位置:光盘\TM\sl\9\9.36)
SELECT * FROM info WHERE name REGEXP ‘[ceo]’;
代码执行结果如图9.52所示。

图9.52 匹配指定字符中的任意一个
8****.8.2 使用“*”和“+”来匹配多个字符
正则表达式中,“”和“+”都可以匹配多个该符号之前的字符。但是,“+”至少表示一个字符,而“”可以表示0个字符。
例9.37下面从info表name字段中查询字母‘c’之前出现过‘a’的记录。SQL代码如下:(实例位置:光盘\TM\sl\9\9.37)
SELECT * FROM info WHERE name REGEXP 'a*c';
代码执行结果如图9.53所示。

图9.53 使用“*”来匹配多个字符
查询结果显示,Aric、Eric和Lucy中的字母c之前并没有a。因为“”可以表示0个,所以“ac”表示字母c之前有0个或者多个a出现。上述的情况都是属于前面出现过0个的情况。如果使用“+”,其SQL代码如下:
SELECT * FROM info WHERE name REGEXP ’a+c’;
代码执行结果如图9.54所示。

图9.54 使用“+”来匹配多个字符
查询结果只有一条。只有Jack是刚好字母c前面出现了a。因为a+c表示字母c前面至少有一个字母a。
8****.8.3 匹配以指定的字符开头和结束的记录
正则表达式中,^表示字符串的开始位置,$表示字符串的结束位置。下面将通过一个具体的实例演示如何匹配以指定的字符开头和结束的记录。
例9.38在学生成绩信息表computer_stu中查找姓名(name)字段中以L开头、以y结束的,中间包含两个字符的学生的成绩信息,运行结果如图9.55所示。(实例位置:光盘\TM\sl\9\9.38)

图9.55 使用正则表达式查询学生成绩信息
要实现查询姓名(name)字段中以L开头、以y结束的,中间包含两个字符的学生成绩,可以通过正则表达式查询来实现。其中正则表达式中,^表示字符串的开始位置,$表示字符串的结束位置,.表示除“\n”以外的任何单个字符,具体代码如下。
SELECT * FROM computer_stu WHERE name REGEXP '^L..y$';
8****.9 小结
本章对MySQL数据库常见的查询方法进行了详细讲解,并通过大量的举例说明,帮助读者更好地理解所学知识的用法。在阅读本章时,读者应该重点掌握多条件查询、连接查询、子查询和查询结果排序。本章学习的难点是使用正则表达式来查询。正则表达式的功能很强大,使用起来很灵活。希望读者能够阅读有关正则表达式的相关知识,能过对正则表达式了解得更加透彻。
8****.10 实践与练习
1.实现从computer_stu表中查询获得一等奖学金的学生的学号、姓名和分数。各个等级的奖学金的最低分存储在score表中。(答案位置:光盘\TM\sl\9\9.39)
2.实现匹配computer_stu表中姓名字段中以J开头的记录。(答案位置:光盘\TM\sl\9\9.40)
第9章 常用函数
MySQL数据库中提供了很丰富的函数。MySQL函数包括数学函数、字符串函数、日期和时间函数、条件判断函数、系统信息函数、加密函数、格式化函数等。函数的执行速度非常快,可以提高MySQL的处理速度,简化用户的操作。本章将详细介绍MySQL函数的相关知识。
通过阅读本章,读者可以
l 了解MySQL函数
l 了解MySQL数学函数的使用方法
l 掌握MySQL字符串函数的使用方法
l 掌握MySQL日期和时间函数的使用方法
l 掌握MySQL条件判断函数的应用方法
l 掌握系统信息函数和加密函数的使用方法
9****.1 MySQL函数
MySQL函数是MySQL数据库提供的内置函数。这些内置函数可以帮助用户更加方便地处理表中的数据。本节将简单地介绍MySQL中包含哪些类别的函数,以及这些函数的使用范围和作用,如表10.1所示。
表10.1 MySQL内置函数类别及作用
MySQL的内置函数不但可以在SELECT查询语句中应用,同样也可以在INSERT、UPDATE和DELECT等语句中应用。例如,在INSERT语句中,应用日期时间函数获取系统的当前时间,并且将其添加到数据表中。MySQL内置函数可以对表中数据进行相应的处理,以便得到用户希望得到的数据。有了这些内置函数可以使MySQL数据库的功能更加强大。下面将对MySQL的内置函数逐一进行详细介绍。
9****.2 数学函数
数学函数是MySQL中常用的一类函数,主要用于处理数字,包括整型和浮点数等。MySQL中内置的数学函数及其作用如表10.2所示。
表10.2 MySQL的数学函数

下面对其中的常用函数进行讲解,并且配合以示例做详细说明。
9****.2.1 ABS(x)函数
ABS(x)函数用于求绝对值。
例10.1使用ABS(x)函数来求5和-5的绝对值,其语句如下。(实例位置:光盘\TM\sl\10\10.1)
select ABS(5),ABS(-5);
查询结果如图10.1所示。

图10.1 使用ABS(x)求数据的绝对值
9****.2.2 FLOOR(x)函数
FLOOR(x)函数返回小于或等于x的最大整数。
例10.2应用FLOOR(x)函数求小于或等于1.5及-2的最大整数,其语句如下。(实例位置:光盘\TM\sl\10\10.2)
select FLOOR(1.5),FLOOR(-2);
其查询结果如图10.2所示。

图10.2 使用FLOOR(x)函数求小于或等于数据的最大整数
9****.2.3 RAND()函数
RAND()函数是返回0~1的随机数。
例10.3运用RAND()函数,获取两个随机数,其语句如下。(实例位置:光盘\TM\sl\10\10.3)
Select RAND(),RAND();
其查询结果如图10.3所示。

图10.3 使用RAND()函数获取随机数
例10.4生成3个1~100之间的随机整数,效果如图10.4所示。(实例位置:光盘\TM\sl\4\10.4)

图10.4 生成3个1~100之间的随机整数
使用ROUND(x)生成一个与数x最接近的整数,当然,也可以使用FLOOR(x)来生成一个小于或者等于x的最大整数。RAND()产生的是随机数,所以每次执行的结果都会是不一样的,具体代码如下。
Select ROUND(RAND()100),FLOOR(RAND()100),CEILING(RAND()*100);
9****.2.4 PI()函数
PI()函数用于返回圆周率。
例10.5使用PI()函数获取圆周率,其语句如下。(实例位置:光盘\TM\sl\10\10.5)
select PI();
查询结果如图10.5所示。

图10.5 使用PI()函数获取圆周率
9****.2.5 TRUNCATE(x,y)函数
TRUNCATE(x,y)函数返回x保留到小数点后y位的值。
例10.6使用TRUNCATE(x,y)函数返回2.123 456 7小数点后3位的值,其语句如下。(实例位置:光盘\TM\sl\10\10.6)
select TRUNCATE(2.1234567,3);
其查询结果如图10.6所示。

图10.6 使用TRUNCATE(x,y)函数获取数据
9****.2.6 ROUND(x)函数和ROUND(x,y)函数
ROUND(x)函数返回离x最近的整数,也就是对x进行四舍五入处理;ROUND(x,y)函数返回x保留到小数点后y位的值,截断时需要进行四舍五入处理。
例10.7使用ROUND(x)函数获取1.6和1.2最近的整数,使用ROUND(x,y)函数获取1.123 456小数点后3位的值,其语句如下。(实例位置:光盘\TM\sl\10\10.7)
select ROUND(1.6),ROUND(1.2),ROUND(1.123456,3);
查询结果如图10.7所示。

图10.7 使用ROUND(x)函数和ROUND(x,y)函数获取数据
9****.2.7 SQRT(x)函数
SQRT(x)函数用于求平方根。
例10.8使用SQRT(x)函数求16和25的平方根,其语句如下。(实例位置:光盘\TM\sl\10\10.8)
select SQRT(16),SQRT(25);
其查询结果如图10.8所示。

图10.8 使用SQRT(x)函数求16和25的平方根
9****.3 字符串函数
字符串函数是MySQL中最常用的一类函数,主要用于处理表中的字符串,其作用如表10.3所示。
表10.3 MySQL的字符串函数

下面对其中的常用函数进行讲解,并且结合例子做详细说明。
9****.3.1 INSERT(s1,x,len,s2)函数
INSERT(s1,x,len,s2)函数将字符串s1中x位置开始长度为len的字符串用字符串s2替换。
例10.9使用INSERT函数将mrkj字符串中的kj替换为book,其语句如下。(实例位置:光盘\TM\sl\10\10.9)
select INSERT('mrkej',3,2,'book');
替换后的查询结果如图10.9所示。

图10.9 使用INSERT(s1,x,len,s2)函数替换指定字符串
9****.3.2 UPPER(s)函数和UCASE(s)函数
UPPER(s)函数和UCASE(s)函数将字符串s的所有字母变成大写字母。
例10.10下面使用UPPER(s)函数和UCASE(s)函数将mrbccd字符串中的所有字母变成大写字母,其语句如下。(实例位置:光盘\TM\sl\10\10.10)
select UPPER('mrbccd'),UCASE('mrbccd');
其转换后的结果如图10.10所示。

图10.10 使用UPPER(s)函数和UCASE(s)函数将mrbccd字符串中的所有字母变成大写字母
9****.3.3 LEFT(s,n)函数
LEFT(s,n)函数返回字符串s的前n个字符。
例10.11应用LEFT函数返回mrbccd字符串的前两个字符,其语句如下。(实例位置:光盘\TM\sl\10\10.11)
select LEFT('mrbccd',2);
其截取结果如图10.11所示。

图10.11 使用LEFT(s,n)函数返回指定字符
9****.3.4 RTRIM(s)函数
RTRIM(s)函数将去掉字符串s结尾处的空格。
例10.12应用RTRIM函数去掉mr结尾处的空格,其语句如下。(实例位置:光盘\TM\sl\10\10.12)
select CONCAT('+',RTRIM(' mr '),'+');
其结果如图10.12所示。

图10.12 使用RTRIM(s)函数去掉mr结尾处的空格
9****.3.5 SUBSTRING(s,n,len)函数
SUBSTRING(s,n,len)函数从字符串s的第n个位置开始获取长度为len的字符串。
例10.13下面使用SUBSTRING函数从mrbccd字符串的第三位开始获取4个字符,结果如图10.13所示。(实例位置:光盘\TM\sl\10\10.13)

图10.13 使用SUBSTRING(s,n,len)函数获取指定长度字符串
9****.3.6 REVERSE(s)函数
REVERSE(s)函数将字符串s的顺序反过来。
例10.14下面使用REVERSE函数将mrbccd字符串的顺序反过来,结果如图10.14所示。(实例位置:光盘\TM\sl\10\10.14)

图10.14 使用REVERSE(s)函数将mrbccd字符串的顺序反过来
9****.3.7 FIELD(s,s1,s2,…)函数
FIELD(s,s1,s2,…)函数返回第一个与字符串s匹配的字符串的位置。
例10.15应用FIELD函数返回第一个与字符串mr匹配的字符串的位置,结果如图10.15所示。(实例位置:光盘\TM\sl\10\10.15)

图10.15 使用FIELD(s,s1,s2,…)函数返回第一个与字符串mr匹配的字符串位置
9****.3.8 LOCATE(s1,s)函数、POSITION(s1 IN s)函数和INSTR(s,s1)函数
在MySQL中,可以通过LOCATE(s1,s)、POSITION(s1 IN s)和INSTR(s,s1)函数获取子字符串相匹配的开始位置。这3个函数的语法格式如下。
(1)LOCATE(s1,s):表示子字符串s1和在字符串s中的开始位置。
(2)POSITION(s1 IN s):表示子字符串s1在字符串s中的开始位置。
(3)INSTR(s,s1):表示子字符串s1在字符串s中的开始位置。
在使用这3个函数时,前两个函数LOCATE(s1,s)和POSITION(s1 IN s)的参数中,是把子字符串作为第一个参数,后第三个函数INSTR(s,s1)则需要把子字符串作为第二个参数,这一点一定不要记错。
例10.16返回字符串“me”在字符串' You love me .He love me '中第一次出现的位置。效果如图10.16所示。(实例位置:光盘\TM\sl\10\10.16)

图10.16 字符串函数的使用
对于字符串函数中的LOCATE(s1,s),POSITION(s1 IN s)是从字符串s中获取s1的开始位置,具体代码如下。
Select LOCATE('me', 'You love me.He love me. ');
Select POSITION('me' IN 'You love me.He love me.');
9****.4 日期和时间函数
日期和时间函数是MySQL中另一最常用的函数,主要用于对表中的日期和时间数据的处理。MySQL内置的日期时间函数及作用如表10.4所示。
表10.4 MySQL的日期和时间函数
9****.4.1 CURDATE()函数和CURRENT_DATE()函数
CURDATE()函数和CURRENT_DATE()函数用于获取当前日期。
例10.17下面使用CURDATE()函数和CURRENT_DATE()函数获取当前日期,其语句如下。(实例位置:光盘\TM\sl\10\10.17)
select CURDATE(),CURRENT_DATE();
其查询结果如图10.17所示。

图10.17 使用CURDATE()和CURRENT_DATE()函数获取当前日期
9****.4.2 CURTIME()函数和CURRENT_TIME()函数
CURTIME()函数和CURRENT_TIME()函数用于获取当前时间。
例10.18下面使用CURTIME()函数和CURRENT_TIME()函数获取当前时间,其语句如下。(实例位置:光盘\TM\sl\10\10.18)
select CURTIME(),CURRENT_TIME();
其查询结果如图10.18所示。

图10.18 使用CURTIME()函数和CURRENT_TIME()函数获取当前时间
9****.4.3 NOW()函数
NOW()函数获取当前日期和时间。还有URRENT_TIMESTAMP()函数、LOCALTIME()函数、SYSDATE()函数和LOCALTIMESTAMP()函数也同样可以获取当前日期和时间。
例10.19下面使用NOW()函数、CURRENT_TIMESTAMP()函数、LOCALTIME()函数、SYSDATE()函数和LOCALTIMESTAMP()函数来获取当前日期和时间,其语句如下。(实例位置:光盘\TM\sl\10\10.19)
select NOW(),CURRENT_TIMESTAMP(),LOCALTIME(),SYSDATE();
运行结果如图10.19所示。

图10.19 使用NOW()、CURRENT_TIMESTAMP()等函数获取当前日期和时间
9****.4.4 DATEDIFF(d1,d2)函数
DATEDIFF(d1,d2)用于计算日期d1与d2之间相隔的天数。
例10.20使用DATEDIFF(d1,d2)函数计算2011-07-05与2011-07-01之间相隔的天数,其语句如下。(实例位置:光盘\TM\sl\10\10.20)
select DATEDIFF('2011-07-05','2011-07-01');
结果如图10.20所示。

图10.20 使用DATEDIFF(d1,d2)函数计算2011-07-05与2011-07-01之间相隔的天数
9****.4.5 ADDDATE(d,n)函数
ADDDATE(d,n)用于返回起始日期d加上n天的日期。
例10.21使用ADDDATE(d,n)函数返回2011-07-01加上3天的日期,结果如图10.21所示。(实例位置:光盘\TM\sl\10\10.21)

图10.21 使用ADDDATE(d,n)函数返回2011-07-01加上3天的日期
9****.4.6 ADDDATE(d,INTERVAL expr type)函数
ADDDATE(d,INTERVAL expr type)函数返回起始日期d加上一个时间段后的日期。
例10.22使用ADDDATE(d,INTERVAL expr type)函数返回2011-07-01加上一年两个月后的日期,其语句如下。(实例位置:光盘\TM\sl\10\10.22)
select ADDDATE('2011-07-01',INTERVAL,'12' YEAR_MONTH);
其运行结果如图10.22所示。

图10.22 使用ADDDATE(d,INTERVAL expr type)函数返回2011-07-01加上一年两个月后的日期
9****.4.7 SUBDATE(d,n)函数
SUBDATE(d,n)函数返回起始日期d减去n天的日期。
例10.23使用SUBDATE(d,n)函数返回2011-07-01减去6天后的日期,结果如图10.23所示。(实例位置:光盘\TM\sl\10\10.23)

图10.23 使用SUBDATE(d,n)函数返回2011-07-01减去6天后的日期
9****.5 条件判断函数
条件函数用来在SQL语句中进行条件判断。根据不同的条件,执行不同的SQL语句。MySQL支持的条件判断函数及作用如表10.5所示。
表10.5 MySQL的条件判断函数
例10.24查询编程词典业绩信息表,如果业绩超过100万,则输出“Very Good”;如果业绩小于100万大于10万,则输出“Popularly”;否则输出“Not Good”。其语句如下。(实例位置:光盘\TM\sl\10\10.24)其查询结果如图10.24所示。
select id,grade, CASE WHEN grade>1000000 THEN 'Very Good' WHEN grade<1000000 and grade >=100000 THEN 'Popularly' ELSE 'Not Good' END level from tb_bccd;

图10.24 条件判断函数的应用
9****.6 系统信息函数
系统信息函数用来查询MySQL数据库的系统信息。例如,查询数据库的版本,查询数据库的当前用户等。如表10.6所示为各种系统信息函数的作用。
表10.6 MySQL的系统信息函数
9****.6.1 获取MySQL版本号、连接数和数据库名的函数
VERSION()函数返回数据库的版本号;CONNECTION_ID()函数返回服务器的连接数,也就是到现在为止MySQL服务的连接次数;DATABASE()函数和SCHEMA()函数返回当前数据库名。
例10.25下面将演示VERSION()、CONNECTION_ID()、DATABASE()和SCHEMA() 4个函数的用法,如图10.25所示。(实例位置:光盘\TM\sl\10\10.25)

图10.25 获取MySQL版本号、连接数和数据库名得函数
其中,VERSION()函数返回的版本号为“5.6.20”;CONNECTOIN_ID()函数返回的连接数为1;DATABASE()函数和SCHEMA()函数返回的当前数据库名是test。
9****.6.2 获取用户名的函数
USER()、SYSTEM_USER()、SESSION_USER()、CURRENT_USER()和CURRENT_USER这几个函数可以返回当前用户的名称。
例10.26查询当前用户的用户名,效果如图10.26所示。(实例位置:光盘\TM\sl\10\10.26)

图10.26 获取用户名的函数
结果显示,当前用户的用户名为root。localhost是主机名。因为服务器和客户端在一台机器上,所以服务器的主机名为localhost。用户名和主机名之间用符号“@”进行连接。
9****.6.3 获取字符串的字符集和排序方式的函数
CHARSET(str)函数返回字符串str的字符集,一般情况下这个字符集就是系统的默认字符集;COLLATION(str)函数返回字符串str的字符排列方式。
例10.27查看字符串'aa'的字符集和字符串排序方式,效果如图10.27所示。(实例位置:光盘\TM\sl\10\10.27)

图10.27 获取字符串的字符集和排序方式的函数
9****.7 加密函数
加密函数是MySQL中用来对数据进行加密的函数。因为数据库中有些很敏感的信息不希望被其他人看到,所以就可以通过加密的方式来使这些数据变成看似乱码的数据。例如,用户密码就应该进行加密。各种加密函数的作用如表10.7所示。
表10.7 MySQL的加密函数
9****.7.1 加密函数PASSWORD(str)
PASSWORD(str)函数可以对字符串str进行加密。一般情况下,PASSWORD(str)函数主要是用来给用户的密码加密的。
例10.28使用PASSWORD(str)函数为字符串'abcd'加密,效果如图10.28所示。(实例位置:光盘\TM\sl\10\10.28)

图10.28 加密函数PASSWORD(str)
结果显示,字符串“abcd”加密后的结果是“*A154C2565E9E7F94BFC08A1FE702624ED 8EFDA” 。PASSWORD(str)函数加密是不可逆的。
PASSWORD(str)函数经常用来给密码加密。MySQL用户需要设置密码,用户不能将未加密的密码直接存储到MySQL的user表中。因为登录MySQL数据库时,数据库系统会将输入的密码先通过PASSWORD(str)函数加密,然后与数据库中的密码进行比较,匹配成功后才可以登录。
9****.7.2 加密函数MD5(str)
MD5(str)函数可以对字符串str进行加密。MD5(str)函数主要对普通的数据进行加密。
例10.29使用MD5(str)函数为字符串'abcd'加密,效果如图10.29所示。(实例位置:光盘\TM\sl\10\10.29)

图10.29 加密函数MD5(str)
结果显示,字符串abcd的MD5值为e2fc714c4727ee9395f324cd2e7f331f。
9****.8 其他函数
MySQL中除了上述内置函数以外,还包含很多函数。例如,数字格式化函数FORMAT(x,n),IP地址与数字的转换函数INET_ATON(ip),还有加锁函数GET_LOCT(nam e,time)、解锁函数RELEASE_LOCK(name)等。在表10.8中罗列了MySQL中支持的其他函数。
表10.8 MySQL的其他函数

9****.8.1 格式化函数FORMAT(x,n)
FORMAT(x,n)函数可以将数字x进行格式化,将x保留到小数点后n位。这个过程需要进行四舍五入。例如,FORMAT(2.356,2)返回的结果将会是2.36;FORMAT(2.353,2)返回的结果将会是2.35。
例10.30使用FORMAT(x,n)函数来将235.345 6和235.345 4进行格式化,都保留到小数点后3位,效果如图10.30所示。(实例位置:光盘\TM\sl\10\10.30)

图10.30 格式化函数FORMAT
结果显示,235.345 6格式化后的结果是235.346;235.345 4格式化后的结果是235.345。这个数都保留到小数点后3位,而且都进行了四舍五入处理。
FORMAT(x,n)函数可以将x保留到小数点后n位。在格式化过程中需要进行四舍五入的操作。FORMAT(x,n)函数与ROUND(x,y)函数返回x保留到小数点后y位的值。截断时需要进行四舍五入处理。
9****.8.2 改变字符集的函数
CONVERT(s USING cs)函数将字符串s的字符集变成cs。
例10.31将字符串'ABC'的字符集变成gbk,效果如图10.31所示。(实例位置:光盘\TM\sl\10\10.31)

图10.31 改变字符集的函数
9****.8.3 改变字段数据类型的函数
CAST(x AS type)和CONVERT(x,type)这两个函数将x变成type类型。这两个函数只对BINARY、CHAR、DATETIME、TIME、SIGNED INTEGER、UNSIGNED INTEGER这些类型起作用。但两种方法只是改变了输出值的数据类型,并没有改变表中字段的类型。
例10.32下面C1表中的times字段为DATETIME类型,将其变为DATE类型或者TIME类型,效果如图10.32所示。(实例位置:光盘\TM\sl\10\10.32)

图10.32 改变字段数据类型的函数
结果显示,times字段原来的取值是2014-09-16 11:05:30,这是DATETIME类型;CAST(times AS DATE)返回的结果是2014-09-16,这说明类型已经变成了DATE型;CONVERT(times,TIME)返回的结果是11:05:30,这说明类型已经变成了TIME类型。
9****.9 小结
本章介绍了MySQL数据库提供的内部函数,包括数学函数、字符串函数、日期和时间函数、条件判断函数、系统信息函数和加密函数等。字符串函数、日期和时间函数是本章的重点内容;条件判断函数是本章的难点,因为条件判断函数涉及很多条件判断和跳转的语句。这些函数通常与SELECT语句一起使用,用来方便用户的查询。同时,INSERT、UPDATE、DELECT语句和条件表达式也可以使用这些函数。
9****.10 实践与练习
1.编写SQL语句,实现利用函数来查看当前数据库的版本号、当前数据库的名称和当前的用户。(答案位置:光盘\TM\sl\10\10.33)
2.编写SQL语句,应用INSTR()函数返回字符串“me”在字符串“You love me .He love me”中第一次出现的位置。(答案位置:光盘\TM\sl\10\10.34)
第10章 索引
索引是一种特殊的数据库结构,是提高数据库性能的重要方式,可以用来快速查询数据库表中的特定记录,MySQL中所有的数据类型都可以被索引。MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。本章将介绍索引的概念、作用、不同类别,用不同的方法创建索引以及删除索引的本章将介绍索引的概念、作用、不同类别,用不同的方法创建索引以及删除索引的方法等。
通过阅读本章,读者可以
l 了解MySQL索引的概念
l 了解MySQL索引的分类
l 掌握在建立数据表时创建索引的方法
l 掌握在已建立的数据表中创建索引的方法
l 掌握修改数据表结构添加索引的方法
l 掌握删除索引的方法
10.1 索引概述
在MySQL中,索引由数据表中一列或多列组合而成,创建索引的目的是为了优化数据库的查询速度。其中,用户创建的索引指向数据库中具体数据所在位置。当用户通过索引查询数据库中的数据时,不需要遍历所有数据库中的所有数据,大幅度提高了查询效率。
10.1.1 MySQL索引概述
索引是一种将数据库中单列或者多列的值进行排序的结构。应用索引,可以大幅度提高查询的速度。
用户通过索引查询数据,不但可以提高查询速度,也可以降低服务器的负载。用户查询数据时,系统可以不必遍历数据表中的所有记录,而是查询索引列。一般过程的数据查询是通过遍历全部数据,并寻找数据库中的匹配记录而实现的。与一般形式的查询相比,索引就像一本书的目录。而当用户通过目录查找书中内容时,就好比用户通过目录查询某章节的某个知识点。这样就为用户在查找内容过程中,缩短大量时间,帮助用户有效地提高查找速度。所以,使用索引可以有效地提高数据库系统的整体性能。
应用MySQL数据库时,并非用户在查询数据的时候,总需要应用索引来优化查询。凡事都有双面性,使用索引可以提高检索数据的速度,对于依赖关系的子表和父表之间的联合查询时,可以提高查询速度,并且可以提高整体的系统性能。但是,创建索引和维护需要耗费时间,并且该耗费时间与数据量的大小成正比;另外,索引需要占用物理空间,给数据的维护造成很多麻烦。
整体来说,索引可以提高查询的速度,但是会影响用户操作数据库的插入操作。因为,向有索引的表中插入记录时,数据库系统会按照索引进行排序。所以,用户可以将索引删除后,插入数据,当数据插入操作完成后,用户可以重新创建索引。
不同的存储引擎定义每个表的最大索引数和最大索引长度。所有存储引擎对每个表至少支持16个索引。总索引长度至少为256字节。有些存储引擎支持更多的索引数和更大的索引长度。索引有两种存储类型,包括B树(BTREE)索引和哈希(HASH)索引。其中,B树为系统默认索引方法。
10.1.2 MySQL索引分类
MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。
1.普通索引
普通索引,即不应用任何限制条件的索引,该索引可以在任何数据类型中创建。字段本身的约束条件可以判断其值是否为空或唯一。创建该类型索引后,用户在查询时,便可以通过索引进行查询。在某数据表的某一字段中,建立普通索引后。用户需要查询数据时,只需根据该索引进行查询即可。
2.唯一性索引
使用UNIQUE参数可以设置唯一索引。创建该索引时,索引的值必须唯一,通过唯一索引,用户可以快速定位某条记录,主键是一种特殊唯一索引。
3.全文索引
使用FULLTEXT参数可以设置索引为全文索引。全文索引只能创建在CHAR、VARC HAR或者TEXT类型的字段上。查询数据量较大的字符串类型的字段时,使用全文索引可以提高查询速度。例如,查询带有文章回复内容的字段,可以应用全文索引方式。需要注意的是,在默认情况下,应用全文搜索大小写不敏感。如果索引的列使用二进制排序后,可以执行大小写敏感的全文索引。
4.单列索引
顾名思义,单列索引即只对应一个字段的索引。其可以包括上述叙述的3种索引方式。应用该索引的条件只需要保证该索引值对应一个字段即可。
5.多列索引
多列索引是在表的多个字段上创建一个索引。该索引指向创建时对应的多个字段,用户可以通过这几个字段进行查询。要想应用该索引,用户必须使用这些字段中的第一个字段。
6.空间索引
使用SPATIAL参数可以设置索引为空间索引。空间索引只能建立在空间数据类型上,这样可以提高系统获取空间数据的效率。MySQL中只有MyISAM存储引擎支持空间检索,而且索引的字段不能为空值。
10.2 创建索引
创建索引是指在某个表中至少一列中建立索引,以便提高数据库性能。其中,建立索引可以提高表的访问速度。本节通过几种不同的方式创建索引。其中包括在建立数据库时创建索引、在已经建立的数据表中创建索引和修改数据表结构创建索引。
10.2.1 在建立数据表时创建索引
在建立数据表时可以直接创建索引,这种方式比较直接,且方便、易用。在建立数据表时创建索引的基本语法结构如下。
create table table_name(
属性名 数据类型[约束条件],
属性名 数据类型[约束条件]
...
属性名 数据类型
[UNIQUE | FULLTEXT | SPATIAL ] INDEX }KEY
[别名]( 属性名1 [(长度)] [ASC | DESC])
);
其中,属性名后的属性值,其含义如下。
(1)UNIQUE:可选项,表明索引为唯一性索引。
(2)FULLTEXT:可选项,表明索引为全文搜索。
(3)SPATIAL:可选项,表明索引为空间索引。
INDEX和KEY参数用于指定字段索引,用户在选择时,只需要选择其中的一种即可;另外别名为可选项,其作用是给创建的索引取新名称;别名的参数如下。
(1)属性名1:指索引对应的字段名称,该字段必须被预先定义。
(2)长度:可选项,指索引的长度,必须是字符串类型才可以使用。
(3)ASC/DESC:可选项,ASC表示升序排列,DESC参数表示降序排列。
1.普通索引创建
创建普通索引,即不添加UNIQUE、FULLTEXT等任何参数。
例11.1下面创建表名为score的数据表,并在该表的id字段上建立索引,其主要代码如下。(实例位置:光盘\TM\sl\11\11.1)
create table score(
id int(11) auto_increment primary key not null,
name varchar(50) not null,
math int(5) not null,
english int(5) not null,
chinese int(5) not null,
index(id));
运行以上代码的结果如图11.1所示。

图11.1 创建普通索引
在命令提示符中使用SHOW CREATE TABLE语句查看该表的结构,在命令提示符中输入的代码如下。
show create table score;
其运行结果如图11.2所示。

图11.2 查看数据表结构
从图11.2中可以清晰地看到,该表结构的索引为id,则可以说明该表的索引建立成功。
2.创建唯一性索引
创建唯一性索引与创建一般索引的语法结构大体相同,但是在创建唯一索引的时候,需要使用UNIQUE参数进行约束。
例11.2创建一个表名为address的数据表,并指定该表的id字段上建立唯一索引,其代码如下所示。(实例位置:光盘\TM\sl\11\11.2)
create table address(
id int(11) auto_increment primary key not null,
name varchar(50),
address varchar(200),
UNIQUE INDEX address(id ASC));
应用SHOW CREATE TABLE语句查看表的结构,其运行如图11.3所示。

图11.3 查看唯一索引的表结构
从图11.3中可以看到,该表的id字段上已经建立了一个名为address的唯一索引。
虽然添加唯一索引可以约束字段的唯一性,但是有时候并不能提高用户查找速度,即不能实现优化查询目的。所以,读者在使用过程中需要根据实际情况来选择唯一索引。
3.创建全文索引
与创建普通索引和唯一索引不同,全文索引的创建只能作用在CHAR、VARCHAR、TEXT类型的字段上。创建全文索引需要使用FULLTEXT参数进行约束。
例11.3创建一个名称为cards的数据表,并在该表的number字段上创建全文索引,其代码如下。(实例位置:光盘\TM\sl\11\11.3)
create table cards(
id int(11) auto_increment primary key not null,
name varchar(50),
number bigint(11),
info varchar(50),
FULLTEXT KEY cards_info(info)) engine=MyISAM;
在命令提示符中应用SHOW CREATE TABLE语句查看表结构,其代码如下。
SHOW CREATE TABLE cards;
运行结果如图11.4所示。

图11.4 查看全文索引的数据表结构
只有MyISAM类型的数据表支持FULLTEXT全文索引,InnoDB或其他类型的数据表不支持全文索引。当用户在建立全文索引的时候,返回“ERROR 1283 (HY000): Column 'number' cannot be part of FULLTEXT index”的错误,则说明用户操作的当前数据表不支持全文索引,即不为MyISAM类型的数据表。
4.创建单列索引
创建单列索引,即在数据表的单个字段上创建索引。创建该类型索引不需要引入约束参数,用户在建立时只需指定单列字段名,即可创建单列索引。
例11.4创建名称为telephone的数据表,并指定在tel字段上建立名称为tel_num的单列索引,其代码如下。(实例位置:光盘\TM\sl\11\11.4)
create table telephone(
id int(11) primary key auto_increment not null,
name varchar(50) not null,
tel varchar(50) not null,
index tel_num(tel(20))
);
运行上述代码后,应用SHOW CREATE TABLE语句查看表的结构,其运行结果如图11.5所示。

图11.5 查看单列索引表的数据表结构
数据表中的字段长度为50,而创建的索引的字段长度为20,这样做的目的是为了提高查询效率,优化查询速度。
5.创建多列索引
与创建单列索引相仿,创建多列索引即指定表的多个字段即可实现。
例11.5创建名称为information的数据表,并指定name和sex为多列索引,其代码如下。(实例位置:光盘\TM\sl\11\11.5)
create table information(
id int(11) auto_increment primary key not null,
name varchar(50) not null,
sex varchar(5) not null,
birthday varchar(50) not null,
INDEX info(name,sex)
);
应用SHOW CREATE TABLE语句查看创建多列的数据表结构,其运行结果如图11.6所示。

图11.6 查看多列索引表的数据结构
需要注意的是,在多列索引中,只有查询条件中使用了这些字段中的第一个字段(即上面示例中的name字段)时,索引才会被使用。
触发多列索引的条件是用户必须使用索引的第一字段,如果没有用到第一字段,则索引不起任何作用,用户想要优化查询速度,可以应用该类索引形式。
6.创建空间索引
创建空间索引时,需要设置SPATIAL参数。同样,必须说明的是,只有MyISAM类型表支持该类型索引。而且,索引字段必须有非空约束。
例11.6创建一个名称为list的数据表,并创建一个名为listinfo的空间索引,其代码如下。(实例位置:光盘\TM\sl\11\11.6)
create table list(
id int(11) primary key auto_increment not null,
goods geometry not null,
SPATIAL INDEX listinfo(goods)
)engine=MyISAM;
运行上述代码,创建成功后,在命令提示符中应用SHOW CREATE TABLE语句查看表的结构。其运行结果如图11.7所示。

图11.7 查看空间索引表的结构
从图11.7中可以看到,goods字段上已经建立名称为listinfo的空间索引,其中,goods字段必须不能为空,且数据类型是GEOMETRY。该类型是空间数据类型。空间类型不能用其他类型代替,否则在生成空间索引时会产生错误且不能正常创建该类型索引。
空间类型除了上述示例中提到的GEOMETRY类型外,还包括如POINT、LINESTRING、POLYGON等类型。这些空间数据类型在平常的操作中很少被用到。
10.2.2 在已建立的数据表中创建索引
在MySQL中,不但可以在用户创建数据表时创建索引,用户也可以直接在已经创建的表中,在已经存在的一个或几个字段上创建索引。其基本的命令结构如下所示。
CREATE [UNIQUE | FULLTEXT |SPATIAL ] INDEX index_name
ON table_name(属性 [(length)] [ ASC | DESC]);
命令的参数说明如下。
(1)index_name为索引名称,该参数作用是给用户创建的索引赋予新的名称。
(2)table_name为表名,即指定创建索引的表名称。
(3)可选参数,指定索引类型,包括UNIQUE(唯一索引)、FULLTEXT(全文索引)、SPATIAL(空间索引)。
(4)属性参数,指定索引对应的字段名称。该字段必须已经预存在用户想要操作的数据表中,如果该数据表中不存在用户指定的字段,则系统会提示异常。
(5)length为可选参数,用于指定索引长度。
(6)ASC和DESC参数,指定数据表的排序顺序。
与建立数据表时创建索引相同,在已建立的数据表中创建索引同样包含6种索引方式。
1.创建普通索引
例11.7首先,应用SHOW CREATE TABLE语句查看studentinfo表的结构,其运行结果如图11.8所示。(实例位置:光盘\TM\sl\11\11.7)

图11.8 查看未添加索引前的表结构
然后,在该表中创建名称为stu_info的普通索引,在命令提示符中输入如下命令。
create INDEX stu_info ON studentinfo(sid);
输入上述命令后,应用SHOW CREATE TABLE语句查看该数据表的结构。其运行结果如图11.9所示。

图11.9 查看添加索引后的表格结构
从图11.9中可以看出,名称为stu_info的数据表创建成功。如果系统没有提示异常或错误,则说明已经向studentinfo数据表中建立名称为stu_info的普通索引。
2.创建唯一索引
在已经存在的数据表中建立唯一索引的命令如下。
CREATE UNIQUE INDEX 索引名 ON 数据表名称(字段名称);
其中,UNIQUE是用来设置索引唯一性的参数,该表中的字段名称既可以存在唯一性约束,也可以不存在唯一性约束。
例11.8下面在index1表中的cid字段上建立名为index1_id的唯一性索引,代码如下。(实例位置:光盘\TM\sl\11\11.8)
CREATE UNIQUE INDEX index1_id ON index1(cid);
输入上述命令后,应用SHOW CREATE TABLE语句查看该数据表的结构。其运行结果如图11.10所示。

图11.10 查看添加唯一索引后的表格结构
3.创建全文索引
在MySQL中,为已经存在的数据表创建全文索引的命令如下。
CREATE FULLTEXT INDEX 索引名 ON 数据表名称(字段名称);
其中,FULLTEXT用来设置索引为全文索引。操作的数据表类型必须为MyISAM类型。字段类型必须为VARCHAR、CHAR、TEXT等类型。
例11.9下面在index2表中的info字段上建立名为index2_info的全文索引,代码如下。(实例位置:光盘\TM\sl\11\11.9)
CREATE FULLTEXT INDEX index2_info ON index2(info);
输入上述命令后,应用SHOW CREATE TABLE语句查看该数据表的结构。其运行结果如图11.11所示。

图11.11 查看添加全文索引后的表格结构
4.创建单列索引
与建立数据表时创建单列索引相同,用户可以设置单列索引。其命令结构如下。
CREATE INDEX 索引名 ON 数据表名称(字段名称(长度));
设置字段名称长度,可以优化查询速度,提高查询效率。
例11.10下面在index3表中的address字段上建立名为index3_addr的单列索引。Address字段的数据类型为varchar(20),索引的数据类型为char(4),代码如下。(实例位置:光盘\TM\sl\11\11.10)
CREATE INDEX index3_addr ON index3(address(4));
输入上述命令后,应用SHOW CREATE TABLE语句查看该数据表的结构。其运行结果如图11.12所示。

图11.12 查看添加单列索引后的表格结构
5.创建多列索引
建立多列索引与建立单列索引类似。其主要命令结构如下。
CREATE INDEX 索引名 ON 数据表名称(字段名称1,字段名称2,…);
与建立数据表时创建多列索引相同,当创建多列索引时,用户必须使用第一字段作为查询条件,否则索引不能生效。
例11.11下面在index4表中的name和address字段上建立名为index4_na的多列索引,代码如下。(实例位置:光盘\TM\sl\11\11.11)
CREATE INDEX index4_na ON index4(name,address);
输入上述命令后,应用SHOW CREATE TABLE语句查看该数据表的结构。其运行结果如图11.13所示。

图11.13 查看添加多列索引后的表格结构
6.创建空间索引
建立空间索引,用户需要应用SPATIAL参数作为约束条件。其命令结构如下。
CREATE SPATIAL INDEX 索引名 ON 数据表名称(字段名称);
其中,SPATIAL用来设置索引为空间索引。用户要操作的数据表类型必须为MyISAM类型。并且字段名称必须存在非空约束,否则将不能正常创建空间索引。
10.2.3 修改数据表结构添加索引
修改已经存在表上的索引,可以通过ALTER TABLE语句为数据表添加索引,其基本结构如下。
ALTER TABLE table_name ADD [ UNIQUE | FULLTEXT |SPATIAL ] INDEX index_name(属性名 [(length)][ASC | DESC]);
该参数与11.2.1节和11.2.2节中所介绍的参数相同,这里不再赘述,请读者参阅前面两节中的内容。
1.添加普通索引
首先,应用SHOW CREATE TABLE语句查看studentinfo表的结构,其运行结果如图11.14所示。

图11.14 查看未添加索引前的表结构
然后,在该表中添加名称为timer的普通索引,在命令提示符中输入如下命令。
alter table studentinfo ADD INDEX timer (time(20));
输入上述命令后,应用SHOW CREATE TABLE语句查看该数据表的结构。其运行结果如图11.15所示。

图11.15 查看添加索引后的表格结构
从图11.15中可以看出,名称为timer的数据表添加成功,已经成功向studentinfo数据表中添加名称为timer的普通索引。
从功能上看,修改数据表结构添加索引与在已存在数据表中建立索引所实现功能大体相同,二者均是在已经建立的数据表中添加或创建新的索引。所以,用户在使用的时候,可以根据个人需求和实际情况,选择适合的方式向数据表中添加索引。
2.添加唯一索引
与已存在的数据表中添加索引的过程类似,在数据表中添加唯一索引的命令结构如下所示。
ALTER TABLE 表名 ADD UNIQUE INDEX 索引名称*(字段名称);
其中,ALTER语句一般是用来修改数据表结构的语句,ADD为添加索引的关键字;UNIQUE是用来设置索引唯一性的参数,该表中的字段名称既可以存在唯一性约束,也可以不存在唯一性约束。
3.添加全文索引
创建全文索引与创建普通索引和唯一索引不同,全文索引创建只能作用在CHAR、VARCHAR、TEXT类型的字段上。创建全文索引需要使用FULLTEXT参数进行约束。
在MySQL中,为已经存在的数据表添加全文索引的命令如下。
ALTER TABLE 表名 ADD FULLTEXT INDEX 索引名称(字段名称);
其中,ADD是添加的关键字,FULLTEXT用来设置索引为全文索引。操作的数据表类型必须为MyISAM类型。字段类型同样必须为VARCHAR、CHAR、TEXT等类型。
例11.12使用ALTER INDEX语句在数据表workinfo的address字段上创建名为index_ext的全文索引。(实例位置:光盘\TM\sl\11\11.12)
用修改数据表结果的方式添加全文索引。用ALTER INDEX语句在address字段上创建名为index_ext的全文索引,具体代码如下。
ALTER TABLE workinfo ADD FULLTEXT INDEX index_ext(address);
输入上述命令后,应用SHOW CREATE TABLE语句查看该数据表的结构。其运行结果如图11.16所示。

图11.16 查看使用ALTER TABLE语句创建的全文索引
4.添加单列索引
与建立数据表时创建单列索引相同,用户可以设置单列索引。其命令结构如下。
ALTER TABLE 表名 ADD INDEX 索引名称(字段名称(长度));
同样,用户可以设置字段名称长度,以便优化查询速度,提高执行效率。
5.添加多列索引
添加多列索引与建立单列索引类似。其主要命令结构如下。
ALTER TABLE 表名 ADD INDEX 索引名称(字段名称1,字段名称2,…);
使用ALTER修改数据表结构同样可以添加多列索引。与建立数据表时创建多列索引相同,当创建多列索引时,用户必须使用第一字段作为查询条件,否则索引不能生效。
6.添加空间索引
添加空间索引,用户需要应用SPATIAL参数作为约束条件。其命令结构如下。
ALTER TABLE 表名 ADD SPATIAL INDEX 索引名称(字段名称);
其中,SPATIAL用来设置索引为空间索引。用户要操作的数据表类型必须为MyISAM类型,并且字段名称必须存在非空约束,否则将不能正常创建空间索引。该类别索引并不常用,初学者只需要了解该索引类型即可。
10.3 删除索引
在MySQL中,创建索引后,如果用户不再需要该索引,则可以删除指定表的索引。因为这些已经被建立且不常使用的索引,一方面可能会占用系统资源,另一方面也可能导致更新速度下降,这极大地影响了数据表的性能。所以,在用户不需要该表的索引时,可以手动删除指定索引。其中,删除索引可以通过DROP语句来实现。其基本的命令如下。
DROP INDEX index_name ON table_name;
其中,参数index_name是用户需要删除的索引名称,参数table_name指定数据表名称,下面应用示例向读者展示如何删除数据表中已经存在的索引。打开MySQL后,应用SHOW CREATE TABLE语句查看数据表的索引,其运行结果如图11.17所示。

图11.17 查看address数据表内的索引
从图11.17中可以看出,名称为address的数据表中存在唯一索引address。在命令提示符中继续输入如下命令。
DROP INDEX id ON address
运行上述代码的结果如图11.18所示。
在用户顺利删除索引后,为确定该索引是否已被删除,用户可以再次应用SHOW CREATE TABLE语句来查看数据表结构。其运行结果如图11.19所示。

图11.18 删除唯一索引address

图11.19 再次查看address数据表结构
从图11.19可以看出,名称为address的唯一索引已经被删除。
例11.13本实例将使用DROP语句从数据表中删除不再需要的索引,效果如图11.20所示。(实例位置:光盘\TM\sl\11\11.13)

图11.20 删除唯一性索引
使用DROP语句删除workinfo表的唯一性索引index_id,具体代码如下。
DROP INDEX index_id ON workinfo;
10.4 小结
本章对MySQL数据库的索引的基础知识、创建索引、删除索引进行了详细讲解,创建索引的内容是本章的重点。读者应该重点掌握创建索引的3种方法,分别为创建表的时候创建索引、使用CREATE INDEX语句来创建索引和使用ALTER TABLE语句来创建索引。
10.5 实践与练习
1.运用CREATE INDEX语句为name字段创建长度为10的索引index_name。(答案位置:光盘\TM\sl\11\11.14)
2.创建一个表名为tb_user的数据表,并在该表的id字段上建立唯一索引。(答案位置:光盘\TM\sl\11\11.15)
第1****1章 视图
视图是从一个或多个表中导出来的表,是一种虚拟存在的表。视图就像一个窗口,通过这个窗口可以看到系统专门提供的数据。这样,用户可以不用看到整个数据库表中的数据,而只关心对自己有用的数据。视图可以使用户的操作更方便,而且可以保障数据库系统的安全性。本章将介绍视图的含义和作用,视图定义的原则和创建视图的方法,并对修改视图、查看视图和删除视图的方法进行了详细的讲解。
通过阅读本章,读者可以:
l 了解使用CREATE VIEW 语句创建视图的方法
l 了解创建视图的注意事项
l 掌握使用SHOW TABLE STATUS 语句查看视图的方法
l 掌握使用CREATE OR REPLACE VIEW 语句修改视图的方法
l 掌握使用ALTER 语句修改视图的方法
l 掌握更新视图和使用DROP VIEW 语句删除视图的方法
11.1 视图****概述
视图是有数据库中的一个表或多个表导出的虚拟表,方便用户对数据的操作。本节将详细讲解视图的概念及作用。
11.1.1 视图****的概念
视图是一个虚拟表,是从数据库中一个或多个表中导出来的表,其内容有查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,数据库中之存放了视图的定义,而并没有存放视图中的数据。这些数据存放在原来的表中。使用视图查询数据时,数据库系统会从原来的表中去除对应的数据。因此,视图中的数据是依赖于原来的表中的数据的。一旦表中的数据发生改变,显示在视图的数据也会发生改变。
视图是存储在数据库中的查询的SQL语句,它主要出于两种原因:安全原因,视图可以隐藏一些数据,例如,员工信息表,可以用视图只显示姓名、工龄、地址,而不显示社会保险号和工资数等;另一个原因是可使复杂的查询易于理解和使用。
11.1.2 视图****的作用
对其中所引用的基础表来说,视图的作用类似于筛选。定义视图的筛选可以来自当前或其他数据库的一个或多个表,或者其他试图。通过视图进行查询没有任何限制,通过它们进行数据修改时的限制也很少。下面将视图的作用归纳为如下几点。
\1. 简单性
看到的就是需要的。视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。
\2. 安全性
视图的安全性可以防止未授权用户查看特定的行或列,是有权限用户只能看到表中特定行的方法如下。
(1) 在表中增加一个标志用户名的列。
(2) 建立视图,使用户只能看到标有自己用户名的行。
(3) 把视图授权给其他用户。
\3. 逻辑****数据独立性
视图可以是应用程序和数据库表在一定程度上独立。如果没有视图,程序一定是建立在表上的。有了视图之后,程序可以建立在视图之上,从而程序与数据库表被视图分割开来。视图可以在一下几个方面程序与数据独立。
(1) 如果应用建立在数据库表上,当数据库表发生变化时,可以在表上建立视图,通过视图屏蔽表的变化,从而使应用程序可以不动。
(2) 如果应用建立在数据库表上,当应用发生变化时,可以在表上建立视图,通过视图屏蔽应用的变化,从而使数据库表不动。
(3) 如果应用建立在视图上,当数据库表发生变化时,可以在表上修改视图,通过视图屏蔽表的变化,从而使应用程序可以不动。
(4) 如果应用建立在视图上,当应用发生变化时,可以在表上修改视图,通过视图屏蔽应用的变化,从而使数据库可以不动。
11.2 创建视图
11.2.1 查看****创建视图的权限
创建视图需要具有CREATE VIEW的权限,同事应该具有查询设计的列的SELECT权限。可以使用SELECT语句来查询这些权限信息,查询语法如下。
SELECT Selete_priv,Create_view_priv FROM mysql,user WHERE user=’用户名’****;
(1) Selete_priv属性表示用户是否具有SELECT权限,Y表示拥有SELECT权限,N表示没有。
(2) Create_view_priv属性表示具有CREATE VIEW权限;mysql.user表示MySQL数据库下面的user表。
(3) “用户名”参数表示要查询是否拥有DROP权限的用户,该参数需要用单引号引起来。
例 11.1 下面查询MySQL中root用户是否具有创建视图的权限,代码如下。(实例位置:光盘/TM/11/11.1)
SELECT Select_priv, Create_view_priv FROM mysql.user WHOERE user=****’root’;
执行结果如图11.1所示。

图11.1 查看用户是否具有创建视图的权限
结果中select_priv和create_view_priv属性的值都为Y,这表示root用户具有SELECT和CREATE VIEW 权限。
11.2.2 创建****视图的步骤
MySQL中,创建视图是通过CREATE VIEW语局实现的,其语法如下。
CREATE [ALGORITHM={UNDEFINED|MERGE|TEMPTABLE}]
VIEW 视图名[{属性清单}****]
AS SELECT 语句
[WITH[CASCADED|LOCAL]CHECK OPTION];
(1) ALGORITHM是可选参数,表示视图选择的算法;
(2) “视图名”参数表示要创建的视图名称;
(3) “属性清单”是可选参数,指定视图中各个属性的名词,默认情况下与SELECT语句中查询的属性相同;
(4) SELECT语句参数是一个完整的查询语句,表示从某个表中查出某些满足条件的记录,将这些记录导入视图中;
(5) WITH CHECK OPTION是可选参数,表示更新视图时要保证在该视图的权限范围之内。
例 11.2 在数据表tb_book中创建view1 视图,视图命名为book_view1,并设置视图属性分别为a_sort、a_talk、a_books,代码如下。(实例位置:光盘/TM/sl/11/11.2)
CREATE VIEW
book_view1(a_sort,a_talk,a_books)
AS SELECT sor,talk,books
FROM tb_book
执行结果如果11.2所示。

图11.2 创建视图book_view1
如果要在tb_book表和tb_user表上创建名为book_view1的视图,执行代码如下。
CREATE ALGORITHM=MERGE VIEW
book_view1(a_sort,a_talk.a_books,a_name)
AS SELECT sort.talk,books,tb_user.name
FROM tb_book,tb_name,WHERE tb_book.id=tb_name.id
WITH LOCAL CHECK OPTION;
例 11.3 在实际项目开发过程中的数据表中可能有很多的字段,但某个模块可能只需要其中的几个。为了提高查询速度和简化操作,可以将该模块需要的字段单独提取出来放在某个视图中,例如,本实例涉及学生表和成绩表,在建立的视图中只含有与学生成绩有关的字段。如图11.3所示。(实例位置:光盘/TM/sl/11/11.3)

图11.3 创建视图
运行本实例,通过MySQL视图查询学生成绩信息,运行结果如图11.4所示,查询结果显示的内容即为视图中所有字段的内容。

图11.4 学生成绩列表
(1) 创建视图scoreinfo,通过该视图显示学生成绩信息,该视图的创建代码如下所示。
c****reate view scoreinfo as select sno,sname,yw,wy,sx from tb_student,tb_score where tb_student.id=tb_score.sid
(2) 建立数据库连接文件conn.php实现与MySQL数据库的连接,并设置数据库字符集为UTF-8,代码如下。
<?php
$conn=new mysqli(“localhost”,”root”,”root”,”db_database11”); //连接数据库
$conn->query(“set names utf8”)****; //设置编码****格式
?>
(3) 查询视图scoreinfo中的内容,并显示查询结果,代码如下。
query(“select\*from scoreinfo”);** **//****执行****查询** **$info=$sql->fetch_array(****MYSQLI_ASSOC****)****;** **//****获得****查询结果集** **if($info==NULL){** **//判断****是否查询到成绩信息** **echo”****暂无****学生****信息****”;** **}else{** **do{** **//通过****循环打印学生****成绩****信息** **?>**
<?php
}while($info=$sql->fetch_array(MYSQLI_ASSOC));
}
?>
11.2.3 创建视图****的注意事项
创建视图是需要注意以下几点。
(1) 运行创建视图的语句需要用户具有创建视图(create view)的权限,若加了[or replace]时,还需要用户具有删除视图(drop view)的权限。
(2) SELECT语句不能包含FROM子句中的子查询。
(3) SELECT语句不能引用系统或用户变量。
(4) SELECT语句不能引用预处理语句参数。
(5) 在存储子程序内,定义不能引用子程序参数或局部变量。
(6) 在定义中引用的二表或试图必须存在。但是,创建了视图后,能够舍弃定义引用的表或试图。要想检查视图定义是否存在这类问题,可使用CHECK TABLE语句。
(7) 在定义中不能引用temporary表,不能创建temporary视图。
(8) 在视图定义中命名的表必须已存在。
(9) 不能将触发程序与视图关联在一起。
(10) 在视图定义中允许使用order by,但是,如果从特定视图进行了选择,而该视图使用了具有自己的order by的语句,它将被忽略。
11.3 视图****操作
查看视图是指查看数据库中已存在的视图。查看视图必须要有SHOW VIEW的权限。查看视图的方法主要包括DESCRIBE语句、SHOW TABLE STATUS语句、SHOW CREATE VIEW语句等。本节将主要介绍 这几种查看视图的方法。
\1. DESCRIBE****语句
DESCRIBE可以缩写成DESC,其语法格式如下。
DESCRIBE视图名;
下面使用DESC语句查询book_view1视图中的结构,结果如图11.5所示。

图11.5 使用DESC语句查询book_view1视图中的结构
结果中显示了字段的名称(Field)、数据类型(Type)、是否为空(Null)、是否为主外键(Key)、默认值(Default)和额外信息(Extra)。
\2.
\3.
(1) SHOW TABLE STATUS语句
在MySQL中,可以使用SHOW TABLE STATUS语句查看视图的信息,其语法格式如下。
SHOW TABLE STATUS LIKE’视图名****’;
(1) “LIKE”表示后面匹配的字符串;
(2) “视图名”参数指要查看的视图名称,需要用单引号定义。
例11.4 下面使用SHOW TABLE STATUS语句查看视图book_view1中的信息,代码如下。(实例位置:光盘TM/sl、/11/11.4)
SHOW TABLE STATUSS LIKE ‘book_view1’;
执行结果如图11.6所示。

图11.6 使用SHOW TABLE STATUS语句查看视图book_view1的信息
从执行结果可以看出,存储阴晴、数据长度等信息都显示为NULL,则说明视图为虚拟表,与普通数据表是有区别的。下面使用SHOW TABLE STATUS语句来查看tb_book表的信息,执行结果如图11.7所示。

图11.7 使用SHOW TABLE STATUS语句来查看tb_book表的信息
从上面的结果中可以看出,数据表的信息都已经显示出来了,这就是视图和普通数据表的区别。
(2) SHOW CREATE VIEW语句
在MySQL中,SHOW CREATE VIEW语句可以查看视图的详细定义,其语法格式如下。
SHOW CREATE VIEW视图****名
例11.5 下面使用SHOW CREATE VIEW语句查看视图book_view1的信息,代码如下。(实例位置:光盘/TM/sl/11/11.5)
S****HOW CREATE VIEW book_view1;
代码执行结果如图11.8所示。

图11.8 使用SHOW CREATE VIEW语句查看视图book_view1的信息
通过SHOW CREATE VIEW语句,可以查看视图的所有信息。
11.3.2修改视图
修改视图是指修改数据库中已存在的表的定义。当基本表的某些字段发生改变时,可以通过修改视图来保持视图和基本表之间一致。MySQL中通过CREATE OR REPLACE VIEW语句和ALTER语句来修改视图。下面介绍这两种修改视图的方法。
(1) CREATE OR REPLACE VIEW
在MySQL中,CREATE OR REPLACE VIEW语句可以用来修改视图。该语句的使用非常灵活。在视图已经存在的情况下,对视图铏修改;视图不存在时,可以创建视图。CREATE OR REPLACE VIEW语句的语法如下。
CREA****TE OR REPLACE[ALGORITHM={UNDEFINED|MERGE|TEMPTABLE}]
VIEW 视图{属性清单}
AS SELECT 语句
[WITH[CASCADED|LOCAL]CHECK OPTION]****;
例11.6 下面使用CREATE OR REPLACE VIEW 语句将视图bood_view1的字段修改为a_sort和a_book,执行结果如图11.9所示。(实例位置:光盘/TM/sl/11/11.6)

图11.9 使用CREATE OR REPLACE VIEW语句修改视图
使用DESC语句查询book_view1视图,结果如图11.10所示。

图11.10 使用DESC语句查询book_view1
从上面的结果中可以看出,修改后的book_viwq1中只有两个字段。
(2) ALTER
ALTER VIEW语句改变了视图的定义,包括被索引的视图,但不影响所依赖的存储过程或触发器。该语句与CREATE VIEW语句有着同样的限制,如果删除并重建了一个视图,就必须重新为它分配权限。
ALTER VIEW语句的语法如下。
a****lter view [algorithm={merge | temptable | undefined}]view view_name[(column_list)]as select_statement[with[cascaded | local]check option]
(1) algorithm:该参数已经在创建视图中作了介绍,这里不再赘述。
(2) view_name:视图的名称。
(3) select_statement:SQL语句用于限定视图。

例11.7 下面将对book_view1视图进行修改,将原有的a_sort和a_book两个属性更改为a_sort一个属性。在更改前,先来查看一下book_view1视图此时包含的属性,结果如图11.11所示。(实例位置:光盘/TM/sl/11/11.7)

图11.11 查看book_view1视图的属性
从结果中可以看出,此时的book_view1视图㕜包含两个属性,下面对视图进行修改,结果如图11.11所示。

图11.11 修改视图属性
结果显示修改成功,下面再来查看一下修改后的视图属性,结果如图11.13所示。

图11.13 查看修改后的视图属性
结果显示,此时视图中只包含一个a_sort属性。
11.3.3 更新视图
对视图的更新其实就是对表的更新,更新视图是指通过视图来插入(INSETR)、更新(UPDATE)和删除(DELETE)表中的数据。因为视图是一个虚拟表,其中没有数据。通过视图更新时,都是转换到基本表来更新。更新视图时,只能更新权限范围内的数据,超出了范围,就不能更新。本节讲解更新视图的方法和更新视图的限制。
\1. 更新视图
例11.8 下面对book_view2视图中的数据进行更新,先来查看book_view2视图中的数据,如图11.14所示。(实例位置:光盘/TM/sl/11/11.8)

图11.14 查看book_view2视图中的数据
下面更新视图中的第27条记录,a_sort的值为“模块类”,a_book的值为“PHP典型模块”,更新语句结果如图11.15所示。

图11.15 更新视图中的数据
结果显示更新成功,下面再来查看一下book_view2视图㕜的数据是否有变化,结果如图11.16所示。

图11.16 查看更新后视图中的数据
下面再来看一下tb_book表中的数据是否有变化,结果如图11.17所示。

图11.17 查看tb_book表中的数据
从上面的结果可以看出,对视图的更新其实就是对基本表的更新。
\2. 更新****视图的限制
并不是所有的视图都可以更新,以下几种情况是不能更新视图的。
(1) 视图中包含COUNT()、SUM()、MAX()、和MIN()等函数。例如:
CREATE VIEW book_view1(a_sort,a_book)
AS SELECT sort,books,COUNT(name)FROM tb_book;
(2) 视图中包含UNION、UNION ALL、DISTINCT、GROUP BY和HAVIG等关键字。例如:
CREATE VIEWE book_view1(a_sort,a_book****)
AS SELECT sort,books,FROM tb_book GROUP BY id;
(3) 常量视图。例如:
CREATE VIEW book_view1
AS SELECT ‘Aric’ as a_book;
(4) 视图中的SELECT中包含字查询。例如:
CREATE VIEW book_view1(a_sort)
AS SELECT(SELECT name FROM tb_book);
(5) 由不可更新的视图导出的视图。例如:
CREATE VIEW book_view1
AS SELECT * FROM book_view2;
(6) 创建视图时,ALGORITHM为TEMPTABLE类型。例如:
CREATE ALGORITHM=TEMPTABLE
VIEW book_view1
AS SELECT * FROM tb_book;
(7) 视图对应的表上存在没有默认值的列,而且该列没有包含在视图里。例如,表中包含name字段没有默认值,但是视图中不包括该字段,那么这个视图是不能更新的。因为,在更新视图时,这个没有默认值的记录将没有值插入,也没有NULL值插入。数据库系统是不会允许这样的情况出现的,其会阻止这个视图更新。
上面的几种汇总情况其实就是一种情况,即视图的数据和基本表的数据不一样了。
11.3.4 删除****视图
删除视图是指删除数据库中已存在的视图。删除视图时,只能删除视图的定义,不会删除数据。MySQL中使用DROP VIEW语句来删除视图。但是,用户必须拥有DROP权限。本节将介绍删除视图的方法。
DROP VIEW 语句的语法如下。
d****rop view if existe<视图名>[RESTRICT|CASCADE****]
(1) IF EXISTS参数指判断视图是否存在,如果存在则执行,不存在则不执行。
(2) “视图名”列表参数表示要删除的视图的名称和列表,各个视图名称之间用逗号隔开。
该语句从数据字典中删除制定的视图定义;如果该视图导出了其他视图,则使用CASCADE级联删除,或者先显式删除导出的视图;删除基表时,由该基表导出的所有视图定义都必须显式删除。
例 11.9 下面删除前面实例中一直使用的book_view1视图,执行语句如下。(实例位置:光盘/TM/sl/11.9)
DROP VIEW IF EXISTS book_view1;
执行结果如图11.18所示。

图11.18 删除视图
执行结果显示删除成功。下面验证一下视图是否真正被删除,执行SHOW CREATE VIEW语句查看,执行结果如图11.19所示。

图11.19 查看视图是否删除成功
结果显示,视图book_view1已经不存在了,说明DROP VIEW语句删除视图成功。
11.4小结
\1. 在department表上创建一个简单的视图,视图名称为department_view1。(答案位置:光盘/TM/sl/11/11.10)
\2. 使用DESCRIBE语句查询视图的结构。(答案位置:光盘/TM/sl/11/11.11)
第12章 数据完整性约束
数据完整性是指数据的正确性和相容性,是为了防止数据库中存在不符合语义的数据,即防止数据库中存在不正确的数据。在MYSQL中提供了多种完整性约束,它们作为数据库关系模式定义的一部分,可以通过CREATE TABLE或ALTER TABLE语句来定义。一旦定义了完整性约束,MYSQL服务器会随时检测处于更新状态的数据库内容是否符合相关的完整性约束,从而保证数据的一致性与正确性。这样,即能有效地防止对数据库的意外破坏,又能提高完整性检测的效率,还能减轻数据库编程人员的工作负担。本章中将对数据完整性约束进行详细介绍。
通过阅读本章,读者可以:
l 掌握定义完整性约束的方法
l 掌握命名完整性约束的方法
l 掌握更新完整性约束的方法
12.1 定义****完整性约束
关系模型的完整性规则是对某种约束条件。在关系模型中,提供了实体完整性、参照完整性和用户定义完整性3项规则。下面将分别介绍MYSQL中对数据库完整性3项规则的设置和实现方式。
12.1.1 实体****完整性
实体(Entity)是一个数据对象,是指客观存在并可以相互区分的事物,如一个教师、一个学生或一个雇员等。一个实体在数据库中表现为表中的一条记录。通常情况下,它必须遵守实体完整性规则。
实体完整性规则(Entity Integrity Rule)是指关系的主属性,即主码(主键)的组成不能为空,也就是关系的主属性不能是空值(NULL)。关系对应于现实世界中的实体集,而现实世界中的实体是可区分的,既说明每个实例具有唯一性标识。在关系模型中,是使用主码(主键)作为唯一性标识的。若假设主码(主键)取空值,则说明这个实体不可标识,即不可区分,这个假设显然不正确,与现实世界应用环境相矛盾,因此不能存在这样的无标识实体,从而在关系模型中引入实体完整性约束。例如,学生关系(学号、姓名、性别)中,“学号”为主码(主键),则“学号”这个属性不能为空值,否则就违反了实体完整性规则。
在MySQL中,实体完整性是通过主键约束和候选键约束来实现的。
\1. 主键****约束
主键可以是表中的某一列,也可以是表中多个列所构成的一个组合;其中,由多个列组合而成的主键也成为复合主键。在MySQL中,主键列必须遵守以下规则。
(1) 每一个表只能定义一个主键。
(2) 唯一性原则。主键的值,也称键值,必须能够唯一标识表中的每一行记录,且不能为NULL。也就是说一张表中两个不同行在主键上不能具有相同的值。
(3) 最小化规则。复合主键不能包含不必要的多余列。也就是说,当从一个复合主键中删除一列后,如果剩下的列构成的主键仍能满足唯一性原则,那么这个复合主键是不正确的。
(4) 一个列名在复合主键的列表中只能出现一次。
在MySQL中,可以在CREATE TABLE或者ALTER TABLE语句中,使用PRIMARY KEY子句来创建主键约束,其实现方式有以下两种。
- 作为列的完整性约束
在表的某个列的属性定义时,加上PRIAMRY KEY关键字实现。
例 12.1 在创建用户信息表tb_user时,将id字段设置为主键,代码如下。(实例位置:光盘/TM/ls/12/12.1)
c****reate table tb_user(
id int auto_increment primary key,
user varchar(30) not null,
password varchar(30****) not null,
createtime datetime);
运行上述代码,其结果如图12.1所示。
- 作为表的完整性约束
在表的所有列的属性定义后,加上PRIMARY KEY(index_col_name,…)子句实现。
例12.2创建学生信息表tb_student时,将学号(id)和所在班级号(class)字段设置为主键,代码如下。(实例位置:光盘/TM/sl/12/12.2)
create table tb_studen(
id int auto_increment,
name varchar(30)not null,
classid int not null,
birthday date,
PRIMARY KEY(id,classid)
);
运行上述代码,其结果如图12.2所示。

图12.1 将id字段设置为主键

图12.2 将id字段和classid字段设置为主键

\2. 候选键****约束
如果一个属性集能唯一标识元组,且又不含有多余的属性,那么这个属性集称为关系的候选键。例如,在包含学号、姓名、性别、年龄、院系、班级等列的“写生信息表”中,“学号”能够标识一名学生,因此,它可以作为候选键,而如果规定,不允许有同名的学生,那么姓名也可以作为候选键。
候选键可以是表中的某一列,也可以是表中多个列所构成的一个组合。任何时候,候选键的值必须是唯一的,且不能为空(NULL)。候选键可以在CREATE TABLE或者ALTER TABLE语句中使用关键字UNIQUE来定义,其实现方法与主键约束类似,也是可作为列的完整性约束或者表的完整性约束两种方式。
在MytSQL中,候选键与主键之间存在以下两点区别。
(1) 一个表正只能创建一个主键,但可以定义若干个候选键。
(2) 定义主键约束时,系统会自动创建PRIMARY KEY索引,而定义候选键约束时,系统会自动创建UNIQUE索引。
例12.3 在创建用户信息表tb_user1时,将id字段user字段设置为候选键,代码如下。(实例位置:光盘/TM/ls/12/12.3)
c****reate table tb_user1(
id int auto_increment UNIQUE,
user varchar(30) not null UNIQUE,
password varchar(30) not null,
createtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
运行上述代码,其结果如图12.3所示。

图12.3 将id字段和user字段设置为候选键
12.1.2参照****完整性
现实世界中的实体之间往往存在着某种联系,在关系模型中,实体及实体间的联系都是用关系来描述的,那么自然就存在着关系与关系见的引用。例如,学生实体和班级实体可以分别用下面的关系表示,其中,主码(主键)用下划线标识。
学生(学生证号、姓名、性别、生日、班级编号、备注)
班级(班级编号、班级名称、备注)
在这两个关系之间存在着属性的引用,即“学生”关系引用了“班级”关系中的主码(主键)“班级编号”。在两个实体间,“班级编号”是“班级”关系的主码(主键),也是“学生”关系的外部码(外键)。显然,“学生”关系中的“班级编号”的值必须是确实存在的班级的“班级编号”,即“班级”关系中的该班级的记录。也就是说“学生”关系汇总某个属性的取值需要参照“班级”关系的属性和值。
参照完整性规则(Referential Integrity Rule)就是定义外码(外键)和主码(主键)之间的引用规则,它使对关系间引用数据的一种限制。
参照完整性的定义为:若属性(或属性组)F是基本关系R的外码,它与基本关系S的主码K相对应,则对于R中每个元组在F上的值值允许两种可能,即要么取空值(F的每个属性值均为空值),要么等于S中某个元组的主码值。其中,关系R与S可以是不同的关系,也可以是同一关系,而F与K是定义再同一个城中。例如,在“学生”关系中每个学生的“班级编号”一项,要么取空值,表示该学生还没有分配班级;要么取值必须与“班级”关系中的某个元组的“班级编号”相同,表示这个学生分配到某个班级学习。者就是参照完整性。如果“学生”关系中,某个学生的“班级编号”取值不能与“班级”关系中任何一个元组的“班级编号”值相同,表示这个学生被分配到不属于所在学校的八级学习。这与实际应用环境不相符,显然是错误的,这就需要在关系模型中定义参照完整性进行约束。
与实体完整性一样,参照完整性也是有系统自动支持的,即在建立关系(表)是,只要定义了“谁是主码”“谁参照于认证”,系统将自动进行此类完整性的检查。在MySQL中,参照完整性可以通过在创建表(CREATE TABLE)或者修改表(ALTER TABLE)是定义一个外键声明来实现。
MySQL有两种常用的引擎烈性(MyISAM和InnoDB),目前,只有InnoDB引擎类型支持外键约束。InnoDB引擎类型中声明外键的基本语法格式如下。
[CONSTRAINT[SYMBOL]]
FOREIGN KEY(index_col_name,…) reference_definition
Reference_definition主要用于定义外键所参照的表、列、参照动作的声明和实施策略等4部分内容,它的基本语法格式如下。
REFERENCES tbl_name[(index_col_name,…)]
**[MATCH FULL| MATCH PARTIAL | MATCH SIMPLE****]** **[ON DELETE reference_option]** **[ON UPDATE reference_option]**
index_col_name的语法格式如下。
col_name[(length)][ASC | DESC****]
reference_option 的语法格式如下。
RESTRICT |CASCADE | SET NULL | NO ACTION
参数说明如下。
-
Index_col_name:用于指定被设置为外键的列。
-
tbl_name:用于指定外键所参照的表名。这个表成为参照表(或父表),而外键所在的表称作参照表(或子表)。
-
col_name:用于指定被参照的列名。外键可以引用被参照表中的主键或候选键,也可以引用被参照表中某些列的一个组合,但这个组合不能是被参照表中随机的一组列,必须保存该组合的取值在参照表中是唯一的,外键中的所有列值在被参照表的列中必须全存在,也就是通过外键来对参照表某些列(外键)的取值进行限定与约束。
-
ON DELETE | ON UPDATE:指定参照动作相关的SQL语句。可为每个外键指定对应于DELETE语句和UPDATE语句的参照动作。
-
Reference_option:指定参照完整性约束的实现策略。其中,当没有明确指定参照完整性的实现策略时,两个参照动作会默认使用RESTRICT。具体的策略可选值如表12.1所示。
表12.1 策略可选值
可 选 值 | 说 明 |
---|---|
RESTRICT | 限制策略:当要删除或更新被参照表中被参照列上,并在外键中出现的值时,系统拒绝对被参照表的删除或更新操作。 |
CASCADE | 级联策略:从被参照表中删除或更新记录行时,自动删除或更新参照表匹配的记录行 |
SET NULL | 置空策略:当从被参照表中删除或更新记录行时,设置参照表中与致对应的外键列的值为NULL,这个策略需要被参照表中的外键列没有声明限定词NOT NULL |
NO ACTION | 不采取实施策略:当一个相关的外键值在被参照表中时,删除或更新被参照中键值的动作不被允许。该策略的动作语言与RESTRICT相同 |
例12.4 创建学生信息表tb_student1,并为其设置参照完整性约束(拒绝删除或更新被参照表中被参照列上的外键值),即将classid字段设置为外键,代码如下。(实例位置:光盘/TM/sl/12/12.4)
create table tb_student1(
id int auto_increment,
name varchar(30) not null,
sex varchar(2),
classsid int not null,
birthday date,
remark varchar(100),
primary key (id),
FOREIGN KEY(classid)
REFERENCES tb_class(id)
ON DELETE RESTRICT
ON UPDATE RESTRICT
);
运行上述代码,其结果如图12.4所示。

图12.4 将classid字段设置为外键

设置外键时,通常需要遵守以下规则。
-
被参照表必须是已经存在的,或者是当前正在创建得到表。如果是当前正在创建的表,也就是说,被参照表与参照表是同一个表,这样的表成为自参照表(Self-referencing Table),这种结构成为自参照完整性(Self-referential Integrity)。
-
必须为被参照表定义主键。
-
必须在被参照表名后面指定列名或列名的组合。这个列或列组合必须是这个被参照表的主键或候选键。
-
外键中列的数目必须和被参照表中的列的数据相同。
-
外键中列的数据类型必须和被参照表的主键(或候选键)中的对应列的数据类型相同。
-
尽管主键是不能够包含空值的,但允许在外键中出现一个控制。这意味着,只要外键的每个非空值出现在指定的主键中,这个外键的内容就是正确的。
12.1.3 用户****定义完整性
用户定义完整性规则(User-defined Integrity Rule)是针对某一因公环境的完整性约束条件,它反映了某一具体应用所涉及的数据应满足的要求。关系模型提供定义和检验这类完整性规则的机制,其目的是由系统来统一处理,而不再由应用程序来完成这项工作。在实际系统中,这类完整性规则一般是在建立数据表的同时进行定义,应用编程人员不需要在做考虑,如果某些约束条件没有建立在库表一级,则应用编程人员应在各模块的具体编程中通过程序进行检查和控制。
MySQL支持非空约束、CHIECK约束和触发器3中用户自定义完整性约束。其中,触发器将第15张进行详细介绍。这里主要介绍非空约束和CHECK约束。
\1. 非空****约束
在MySQL中,非空约束可以通过在CEATE TABLE或ALTER TABLE语句中,某个列定义后面加上关键字NOT NULL来定义,用来约束该列的取值不能为空。
例 12.5 创建班级信息表tb_class1,并为其name字段添加非空约束,代码如下。(实例位置:光盘/TM/sl/12/12.5)
CREATE TABLE tb_class1(
id int (11) **NOT NULL** AUTO_INCREMENT, name varchar(45) **NOT NULL,** remark varchar(100) DEFAULT NULL PRIMARY KEY(`id`) );
运行上述代码,其结果如图12.5所示。

图12.5 为name字段添加非空约束
\2. CHECK****约束
与非空约束一样,CHECK约束也可以通过在CREATE TABLE 或ALTER TABLE语句中,根据用户的事迹完整性要求来定义。它可以分别对列或表实施CHECK约束,其中使用的语法如下。
CHECK(expr)
其中,expr是一个SQL表达式,用于指定需要检查的限定条件。在更新表数据时,MySQL会检查更新后的数据行是否满足CHECK约束中的限定条件。该限定条件可以是简单的表达式,也可以使复杂的表达式(子查询)。
下面将分别介绍如何对列和表实施CHECK约束。
- 对列实施CHECK约束
将CHECK子句置于表的某个列的定义之后就是对列实施CHECK约束。下面将通过一个具体的实例俩说明如何对列实施CHECK约束。
例12.6 创建学生信息表tb_student2,限制其age字段的值只能在7~18之间,(不包括18)的数,代码如下。(实例位置:光盘/TM/sl/12.12.6)
create table tb_student2(
id int auto_increment,
name varchar(30) not null,
sex varchar(2),
age int not null CHECK(age>6 and age<18),
remark varchar(100),
primary key(id)
);
运行上述代码,其结果如图12.6所示。

图12.6 对列实施CHECK约束

- 对表实施CHECK约束
将CHECK子句置于表中所有列的定义以及逐渐约束和外键定义之后就是对表实施CHECK约束。下面通过一个具体的实例来说明如何对表实施CHECK约束。
例12.7 创建学生信息表tb_student3,限制其classid字段的值只能是tb_class表中id字段某一个id值,代码如下。(实例位置:光盘/TM/sl/12/12.7)
create table tb_student3(
id int auto_increment,
name varchar(30) not null,
sex varchar(2),
classid int not null,
birthday date,
remark varchar(100),
primary key(id),
CHECK(classid IN(SELECT id FROM tb_class))
);
运行上述代码,其结果如图12.7所示。

图12.7 对表实施CHECK约束
12.2 命名****完整性约束
在MySQL中,也可以对完整性约束进行添加、修改和删除等操作。其中,为了删除和修改完整性约束,需要在定义约束的同事对其进行命名。命名完整性约束的方式是在各种完整性约束的定义说明之前加上CONSTRAINT子句实现的。CONSTRAINT子句的语法格式如下。
CONSTRAINT
[PRIMAR KEY 短句 | FOREIGN KEY 短句 | CHECK 短句]
参数说明如下。
(1) symbol:用于指定约束名称。这个名字是在完整性约束说明的前面被定义,在数据库里必须是唯一的。如果在创建时没有指定约束的名字,则MySQL将自动创建一个约束名字。
(2) PRIMAR KEY短语:主键约束。
(3) FOREIGN KEY短语:参照完整性约束。
(4) CHECK短语:CHECK约束。
例如,对雇员表添加主键约束,并为其命名为PRIMARY,可以使用下面的代码。
ALTER TABLE****雇员表ADDCONSTRAINT PRIMARY
PRIMARY KEY****(雇员编号)
例12.8 修改例12.4的代码,重新创建学生信息表tb_student1,命名为tb_student1a,并为其参照完整性约束命名,代码如下。(实例位置:光盘/TM/sl/12/12.8)
create table tb_student1a(
id int auto_increment PRIMARY KEY,
name varchar(30) not null,
sex varchar(2),
classid int not null,
birthday date,
remark varchar(100),
CONSTRAINT fk_classid FOREIGN KEY(classid)
REFERENCES tb_class(id)
ON DELETE RESTRICT
ON UPDATE RESTRICT
);
运行上述代码,其结果如图12.8所示。

图12.8 命名完整性约束


例12.9在创建表时添加命名外键完整性约束。(实例位置:光盘\TM\sl\12\12.9****)
在本实例中,首先创建一个图书类别信息表,然后再创建一个图书信息表,并为图书信息表设置命名外键约束,实现删除参照表中的数据时,级联删除图书信息表中相关类别的图书信息,具体步骤如下。
(1) 创建名称为tb_type的图书类别信息表,具体代码如下。
CREATE TABLE tb_type (
id int(11) NOT NULL AUTO_INCREMENT, name varchar(45) DEFAULT NULL, remark varchar(100) DEFAULT NULL, **PRIMARY KEY (`id`)**
);
(2) 创建不添加任何外键的教材信息表tb_book,代码如下。
Create table tb_book(id int(11) not null primary key auto_increment,
name varchar(20) not null,
publishingho varchar(20) not null,
author varchar(20),
typeid int(11),
CONSTRAINT fk_typeid
FOREIGN KEY (typeid)
REFERENCES tb_type(id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
运行结果如图12.9所示。

图12.9 在创建表时添加命名外键完整性约束
12.3 更新完整性约束
对各种约束命名后,就可以使用ALTER TABLE语句来更新或删除与列或表有关的各种约束。下面将分别进行介绍。
12.3.1 删除完整性约束
在MySQL中,使用ALTER TABLE语句,可以独立地删除完整性约束,而不会删除表本身。如果使用DROP TABLE语句删除一个表,那么这个表中的所有完整性约束也会自动被删除。删除完整性约束需要在ALTER TABLE语句中使用DROP关键字来实现,具体的语法格式如下。
DROP [FOREIGN KEY| INDEX|
参数说明如下。
(1)FOREIGN KEY:用于删除外键约束。
(2)PRIMARY KEY:用于删除主键约束。需要注意的是,在删除主键时,必须再创建一个主键,否则不能删除成功。
(3)INDEX:用于删除候选键约束。
(4)symbol:要删除的约束名称。
例12.10要删除例12.8中的名称为fk_classid的外键约束,可以使用下面的代码。(实例位置:光盘\TM\sl\12\12.10****)
ALTER TABLE tb_student1a DROP FOREIGN KEY fk_classid;
运行上述代码,其结果如图12.10所示。

图12.10 删除名称为fk_classid的外键约束
12.3.2 修改完整性约束
在MySQL中,完整性约束不能直接被修改,若要修改只能使用ALTER TABLE语句先删除除该约束,然后再增加一个与该约束同名的新约束。由于删除完整性约束的语法在12.3.1节已经介绍了,这里只给出在ALTER TABLE语句中添加完整性约束的语法格式,具体语法格式如下。
ADD CONSTRAINT
参数说明如下。
(1)symbol:为要添加的约束指定一个名称。
(2)各种约束:定义各种约束的语句,具体内容请参见12.1和12.2节介绍的各种约束的添加语法。
例12.11更新例12.8中的名称为fk_classid的外键约束为级联删除和级联更新,可以使用下面的代码。(实例位置:光盘\TM\sl\12\12.11****)
ALTER TABLE tb_student1a DROP FOREIGN KEY fk_classid;
ALTER TABLE tb_student1a
ADD CONSTRAINT fk_classid FOREIGN KEY (classid)
REFERENCES tb_class(id)
ON DELETE CASCADE
ON UPDATE CASCADE
;
运行上述代码,其结果如图12.11所示。

图12.11 更新外键约束
12.4 小结
本章主要介绍了定义完整约束、命名完整性约束、删除完整性约束和修改完整性约束等内容。其中,定义完整性约束和命名完整性约束是本章的重点,需要读者认真学习、灵活掌握,这在以后的数据库设计中非常实用。
12.5 实践与练习
1.在创建用户信息表tb_manager时,将id字段设置为主键。(答案位置:光盘\TM\sl\12\12.11****)
2.创建一个不添加任何外键的教师信息表tb_teacher,然后通过ALTER TABLE语句为其添加一个名称为fk_ departmentid的外键约束。(答案位置:光盘\TM\sl\12\12.12****)
第13章 存储过程与存储函数
存储过程和存储函数是在数据库中定义一些SQL语句的集合,然后直接调用这些存储过程和存储函数来执行已经定义好的SQL语句,可以避免开发人员重复编写相同的SQL语句。而且,存储过程和存储函数是在MySQL服务器中存储和执行的,可以减少客户端和服务器端的数据传输。本章将介绍存储过程和存储函数的含义、作用,以及创建、使用、查看、修改及删除存储过程和存储函数的方法。
通过阅读本章,读者可以:
l 掌握MySQL中存储过程和存储函数的创建方法
l 掌握存储过程和函数的调用、查看、修改和删除的方法
13.1 创建存储过程和存储函数
在数据库系统中,为了保证数据的完整性、一致性,同时也为提高其应用性能,大多数据库常采用存储过程和存储函数技术。MySQL在5.0版本后,也应用了存储过程和存储函数。存储过程和存储函数经常是一组SQL语句的组合,这些语句被当作整体存入MySQL数据库服务器中。用户定义的存储函数不能用于修改全局库状态,但该函数可从查询中被唤醒调用,也可以像存储过程一样通过语句执行。随着MySQL技术的日趋完善,存储过程将和存储函数在以后的项目中得到广泛的应用。
13.1.1 创建存储过程
在MySQL中,创建存储过程的基本形式如下。
CREATE PROCEDURE sp_name ([proc_parameter[...]])
[characteristic ...] routine_body
其中,sp_name参数是存储过程的名称;proc_parameter表示存储过程的参数列表;characteristic参数指定存储过程的特性;routine_body参数是SQL代码的内容,可以用BEGIN...END来标识SQL代码的开始和结束。

一个存储过程包括名字、参数列表,还可以包括很多SQL语句集。下面创建一个存储过程,其代码如下。
delimiter //
create procedure proc_name (in parameter integer)
begin
declare variable varchar(20);
if parameter=1 then
set variable='MySQL';
else
set variable='PHP';
end if;
insert into tb (name) values (variable);
end;
MySQL中存储过程的建立以关键字create procedure开始,后面紧跟存储过程的名称和参数。MySQL的存储过程名称不区分大小写,如PROCE1()和proce1()代表同一存储过程名。存储过程名或存储函数名不能与MySQL数据库中的内建函数重名。
MySQL存储过程的语句块以begin开始,以end结束。语句体中可以包含变量的声明、控制语句、SQL查询语句等。由于存储过程内部语句要以分号结束,所以在定义存储过程前,应将语句结束标志“;”更改为其他字符,并且应降低该字符在存储过程中出现的机率,更改结束标志可以用关键字delimiter定义,例如:
mysql>delimiter //
存储过程创建之后,可用如下语句进行删除,参数proc_name指存储过程名。
drop procedure proc_name
下面创建一个名称为count_of_student的存储过程。首先,创建一个名称为students的MySQL数据库,然后创建一个名为studentinfo的数据表。数据表结构如表13.1所示。
表13.1 studentinfo 数据表结构
字 段 名 | 类型(长度) | 默 认 | 额 外 | 说 明 |
---|---|---|---|---|
sid | INT(11) | Auto_increment | 主键自增型sid | |
name | VARCHAR(50) | 学生姓名 | ||
age | VARCHAR(11) | 学生年龄 | ||
sex | VARCHAR(2) | M | 学生性别 | |
tel | BIGINT(11) | 联系电话 |
例13.1创建一个名称为count_of_student的存储过程,统计studentinfo数据表中的记录数。代码如下。(实例位置:光盘\TM\sl\13\13.1****)
delimiter //
create procedure count_of_student(OUT count_num INT)
reads sql data
begin
select count(*) into count_num from studentinfo;
end
//
在上述代码中,定义一个输出变量count_num,存储过程应用SELECT语句从studentinfo表中获取记录总数,最后将结果传递给变量count_num。存储过程的执行结果如图13.1所示

图13.1 创建存储过程count_of_student
代码执行完毕后,没有报出任何出错信息就表示存储函数已经创建成功。以后就可以调用这个存储过程,数据库中会执行存储过程中的SQL语句。

13.1.2 创建存储函数
创建存储函数与创建存储过程大体相同,创建存储函数的基本形式如下。
CREATE FUNCTION sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body
表13.2 创建存储函数的参数说明
参 数 | 说 明 |
---|---|
Sp_name | 存储函数的名称 |
Fun_parameter | 存储函数的参数列表 |
RETURNS type | 指定返回值的类型 |
Characteristic | 指定存储过程的特性 |
Rountine_body | SQL代码的内容 |
func_parameter可以由多个参数组成,其中每个参数均由参数名称和参数类型组成,其结构如下。
param_name type
param_name参数是存储函数的函数名称;type参数用于指定存储函数的参数类型。该类型可以是MySQL数据库所支持的类型。
例13.2同样,应用studentinfo表,创建名为name_of_student的存储函数,其代码如下。(实例位置:光盘\TM\sl\13\13.2****)
delimiter //
create function name_of_student(std_id INT)
returns varchar(50)
begin
return(select name from studentinfo where sid=std_id);
end
//
上述代码中,存储函数的名称为name_of_student;该函数的参数为std_id;返回值是VARCHAR类型。该函数实现从studentinfo表查询与std_id相同sid值的记录,并将学生名称字段name中的值返回。存储函数的执行结果如图13.2所示。

图13.2 创建name_of_student()存储函数
13.1.3 变量的应用
MySQL存储过程中的参数主要有局部参数和会话参数两种,这两种参数又可以被称为局部变量和会话变量。局部变量只在定义该局部变量的begin…end范围内有效,会话变量在整个存储过程范围内均有效。
1. 局部变量
局部变量以关键字DECLARE声明,后跟变量名和变量类型,例如:
declare a int
当然,在声明局部变量时也可以用关键字DEFAULT为变量指定默认值,例如:
declare a int default 10
下述代码为读者展示如何在MySQL存储过程中定义局部变量以及其使用方法。在该例中,分别在内层和外层BEGIN…END块中都定义同名的变量x,按照语句从上到下执行的顺序,如果变量x在整个程序中都有效,则最终结果应该都为inner,但真正的输出结果却不同,这说明在内部BEGIN…END块中定义的变量只在该块内有效。
例13.3本例说明局部变量只在某个BEGIN…END块内有效,代码如下。(实例位置:光盘\TM\sl\13\13.3****)
delimiter //
create procedure p1()
begin
declare x char(10) default 'outer ';
begin
declare x char(10) default 'inner ';
select x;
end;
select x;
end;
//
上述代码的运行结果如图13.3所示。

图13.3 定义局部变量的运行结果
应用MySQL调用该存储过程的运行结果如图13.4所示。

图13.4 调用存储过程pl()的运行结果
2. 全局变量
MySQL中的会话变量不必声明即可使用,会话变量在整个过程中有效,会话变量名以字符“@”作为起始字符。
例13.4分别在内部和外部BEGIN…END块中都定义了同名的会话变量@t,并且最终输出结果相同,从而说明会话变量的作用范围为整个程序。设置全局变量的代码如下。(实例位置:光盘\TM\sl\13\13.4****)
delimiter //
create procedure p2()
begin
set @t=1;
begin
set @t=2;
select @t;
end;
select @t;
end;
//
上述代码的运行结果如图13.5所示。

图13.5 设置全局变量
应用MySQL调用该存储过程的运行结果如图13.6所示。

图13.6 调用存储过程p2()运行结果
3. 为变量赋值
MySQL中可以使用关键字DECLARE来定义变量,其基本语法如下。
DECLARE var_name[,...] type [DEFAULT value]
DECLARE是用来声明变量的;var_name参数是设置变量的名称。如果用户需要,也可以同时定义多个变量;type参数用来指定变量的类型;DEFAULT value的作用是指定变量的默认值,不对该参数进行设置时,其默认值为NULL。
MySQL中可以使用关键字SET为变量赋值,其基本语法如下。
SET var_name=expr[,var_name=expr] ...
SET关键字是用来为变量赋值;var_name参数是变量的名称;expr参数是赋值表达式。一个SET语句可以同时为多个变量赋值,各个变量的赋值语句之间用“,”隔开。例如,为变量mr_soft赋值,代码如下。
SET mr_soft=10;
另外,MySQL中还可以应用另一种方式为变量赋值,其语法结构如下。
SELECT col_name[,...] INTO var_name[,...] FROM table_name where condition
其中,col_name参数标识查询的字段名称;var_name参数是变量的名称;table_name参数为指定数据表的名称;condition参数为指定查询条件。例如,从studentinfo表中查询name为“LeonSK”的记录,并将该记录下的tel字段内容赋值给变量customer_tel,其关键代码如下。
SELECT tel INTO customer_tel FROM studentinfo WHERE name= 'LeonSK ';
13.1.4 光标的运用
通过MySQL查询数据库,其结果可能为多条记录。在存储过程和函数中使用光标可以实现逐条读取结果集中的记录。光标使用包括声明光标(DECLARE CURSOR)、打开光标(OPEN CURSOR)、使用光标(FETCH CURSOR)和关闭光标(CLOSE CURSIR)。值得一提的是,光标必须声明在处理程序之前,且声明在变量和条件之后。
1. 声明光标
在MySQL中,声明光标仍使用关键字DECLARE,其语法如下。
DECLARE cursor_name CURSOR FOR select_statement
其中,cursor_name是光标的名称,光标名称使用与表名同样的规则;select_statement是一个SELECT语句,返回一行或多行数据。该语句也可以在存储过程中定义多个光标,但是必须保证每个光标名称的唯一性,即每一个光标必须有自己唯一的名称。
通过上述定义来声明光标info_of_student,其代码如下。
DECLARE info_of_student CURSOR FOR SELECT
sid,name,age,sex,age
FROM studentinfo
WHERE sid=1;

2. 打开光标
在声明光标之后,要从光标中提取数据,必须首先打开光标。在MySQL中使用关键字OPEN来打开光标,其基本的语法如下。
OPEN cursor_name
其中,cursor_name参数表示光标的名称。在程序中,一个光标可以打开多次。由于可能在用户打开光标后,其他用户或程序正在更新数据表,所以可能会导致用户在每次打开光标后,显示的结果都不同。
打开上面已经声明的光标info_of_student,其代码如下。
OPEN info_of_student
3. 使用光标
光标在顺利打开后,可以使用FETCH…INTO语句来读取数据,其语法如下。
FETCH cursor_name INTO var_name[,var_name]…
其中,cursor_name代表已经打开光标的名称;var_name参数表示将光标中的SELECT语句查询出来的信息存入该参数中。var_name是存放数据的变量名,必须在声明光标前定义好。FETCH…INTO语句与SELECT…INTO语句具有相同的意义。
将已打开的光标info_of_student中SELECT语句查询出来的信息存入tmp_name和tmp_tel中。其中,tmp_name和tmp_tel必须在使用前定义。其代码如下。
FETCH info_of_student INTO tmp_name,tmp_tel;
4. 关闭光标
光标使用完毕后,要及时关闭,在MySQL中采用关键字CLOSE关闭光标,其语法格式如下。
CLOSE cursor_name
cursor_name参数表示光标名称。下面关闭已打开的光标info_of_student,其代码如下。
CLOSE info_of_student

13.2 存储过程和存储函数的调用
存储过程和存储函数都是存储在服务器的SQL语句的集合。要使用这些已经定义好的存储过程和存储函数就必须要通过调用的方式来实现。对存储过程和函数的操作主要可以分为调用、查看、修改和删除。
13.2.1 调用存储过程
存储过程的调用在前面的示例中多次被用到。MySQL中使用CALL语句来调用存储过程。调用存储过程后,数据库系统将执行存储过程中的语句;然后将结果返回给输出值。CALL语句的基本语法形式如下。
CALL sp_name([parameter[,…]]);
其中,sp_name是存储过程的名称;parameter是存储过程的参数。
13.2.2 调用存储函数
在MySQL中,存储函数的使用方法与MySQL内部函数的使用方法基本相同。用户自定义的存储函数与MySQL内部函数性质相同。区别在于,存储函数是用户自定义的,而内部函数由MySQL自带。其语法结构如下。
SELECT function_name([parameter[,…]]);
例13.5创建存储过程并在PHP中调用该存储过程实现用户注册。(实例位置:光盘\TM\sl\13\13.5****)
(1) 创建pro_reg存储过程,其代码如下。
delimiter //
create procedure pro_reg(in nc varchar(50),in pwd varchar(50),in email varchar(50),in address varchar(50))
begin
insert into tb_reg(name,pwd,email,address) values (nc,pwd,email,address);
end;
//
执行效果如图13.7所示。

图13.7 创建存储过程
(2) 通过PHP预定义类mysqli实现与MySQL数据库的连接,代码如下。
query("set names utf8"); //设置编码格式 $name=$_POST['name']; $pwd=md5($_POST['pwd']); $email=$_POST['email']; $address=$_POST['address']; (3) 调用存储过程pro_reg实现将用户录入的注册信息保存到数据库,代码如下。 if($sql=$conn->query("call pro_reg('".$name."','".$pwd."','".$email."','".$address."')")){ //调用存储过程 echo ""; }else{ echo ""; } } ?>运行本实例,在文本框中输入如图13.8所示的注册信息后,单击“注册”按钮即可将用户填写的注册信息保存到数据库中,最终保存结果如图13.9所示。

图13.8 录入注册信息

图13.9 注册信息被存储到MySQL数据库
13.3 查看存储过程和存储函数
存储过程和存储函数创建以后,用户可以查看存储过程和存储函数的状态和定义。用户可以通过SHOW STATUS语句查看存储过程和存储函数状态,也可以通过SHOW CREATE语句来查看存储过程和存储函数的定义。
13.3.1 SHOW STATUS语句
在MySQL中可以通过SHOW STATUS语句查看存储过程和存储函数的状态。其基本语法结构如下。
SHOW {PROCEDURE | FUNCTION}STATUS[LIKE 'pattern']
其中,PROCEDURE参数表示查询存储过程;FUNCTION参数表示查询存储函数;LIKE 'pattern'参数用来匹配存储过程或存储函数名称。
13.3.2 SHOW CREATE语句
MySQL中可以通过SHOW CREATE语句来查看存储过程和函数的状态,其语法结果如下。
SHOW CREATE{PROCEDURE | FUNCTION } sp_name;
其中,PROCEDURE参数表示存储过程;FUNCTION参数表示查询存储函数;sp_name参数表示存储过程或函数的名称。
例13.6下面查询名为count_of_student的存储过程,其代码如下。(实例位置:光盘\TM\sl\13\13.6****)
show create procedure count_of_student ;
其运行结果如图13.10所示。

图13.10 应用SHOW CREATE语句查看存储过程
查询结果显示存储过程的定义、字符集等信息。

13.4 修改存储过程和存储函数
修改存储过程和存储函数是指修改已经定义好的存储过程和函数。MySQL中通过ALTER PROCEDURE语句来修改存储过程,通过ALTER FUNCTION语句来修改存储函数。
MySQL中修改存储过程和存储函数的语句的语法形式如下。
ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...]
characteristic:
{ CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
其参数说明如表13.3所示。
参 数 | 说 明 |
---|---|
sp_name | 存储过程或函数的名称 |
characteristic | 指定存储函数的特性 |
CONTAINS SQL | 表示子程序包含SQL语句,但不包含读写数据的语句 |
NO SQL | 表示子程序不包含SQL语句 |
READS SQL DATA | 表示子程序中包含读数据的语句 |
MODIFIES SQL DATA | 表示子程序中包含写数据的语句 |
SQL SECURITY | 指明权限执行。DEFINER表示只有定义者自己才能够执行;INVOKER表示调用者可以执行 |
COMMENT’string’ | 是注释信息 |
例13.7下面应用此语句修改存储过程count_of_student,其代码如下。(实例位置:光盘\TM\sl\13\13.7****)
alter procedure count_of_student
modifies sql data
sql security invoker;
其运行结果如图13.11所示。

图13.11 修改存储过程count_of_student的定义

13.5 删除存储过程和存储函数
删除存储过程和存储函数指删除数据库中已经存在的存储过程或存储函数。MySQL中通过DROP PROCEDURE语句来删除存储过程,通过DROP FUNCTION语句来删除存储函数。在删除之前,必须确认该存储过程或函数没有任何依赖关系,否则可能会导致其他与其关联的存储过程无法运行。
删除存储过程和存储函数的语法如下。
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name
其中,sp_name参数表示存储过程或存储函数的名称;IF EXISTS是MySQL的扩展,判断存储过程或函数是否存在,以免发生错误。
例13.8下面删除名称为count_of_student的存储过程,其关键代码如下。(实例位置:光盘\TM\sl\13\13.8****)
drop procedure count_of_student;
删除存储过程count_of_student的运行结果如图13.11所示。

图13.11 删除count_of_student存储过程
例13.9下面删除名称为name_of_student的存储函数,其关键代码如下。(实例位置:光盘\TM\sl\13\13.9****)
drop function name_of_student;
删除存储函数name_of_student的运行结果如图13.12所示。

图13.12 删除name_of_student存储函数
当返回结果没有提示警告或报错时,则说明存储过程或存储函数已经被顺利删除。用户可以通过查询students数据库下的Routines表来确认上面的删除是否成功。
13.6 小结
本章对MySQL数据库的存储过程和存储函数进行了详细讲解,存储过程和存储函数都是用户自己定义的SQL语句的集合。它们都存储在服务器端,只要调用就可以在服务器端执行。本章重点讲解了创建存储过程和存储函数的方法。通过CREATE PROCEDURE语句来创建存储过程,通过CREATE FUNCTION语句来创建存储函数。这两个内容是本章的难点,需要读者将书中的知识点结合实际操作进行练习。
13.7 实践与练习
1.将名称为name_of_student的存储函数的读写权限修改为READS SQL DATA,并加上注释信息“FIND NAME”。(答案位置:光盘\TM\sl\13\13.10****)
2.通过SHOW STATUS语句查看存储函数的状态。(答案位置:光盘\TM\sl\13\13.11****)
第14章 触发器
触发器是由事件来触发某个操作。这些事件包括INSERT语句、UPDATE语句和DELETE语句。当数据库系统执行这些事件时,就会激活触发器执行相应的操作。本章将对触发器的含义、作用,以及创建、查看和删除触发器的方法进行详细介绍。
通过阅读本章,读者可以:
l 了解触发器的概念
l 了解创建单条执行语句的触发器的方法
l 掌握创建多条执行语句的触发器的方法
l 掌握查看触发器的方法
l 掌握使用触发器的方法
l 掌握删除触发器的方法
14.1 MySQL触发器
触发器是由MySQL的基本命令事件来触发某种特定操作,这些基本的命令由INSERT、UPDATE、DELETE等事件来触发某些特定操作。满足触发器的触发条件时,数据库系统就会自动执行触发器中定义的程序语句,可以令某些操作之间的一致性得到协调。
14.1.1 创建MySQL触发器
在MySQL中,创建只有一条执行语句的触发器的基本形式如下。
CREATE TRIGGER 触发器名 BEFORE | AFTER 触发事件
ON 表名 FOR EACH ROW 执行语句
具体的参数说明如下。
(1) 触发器名指定要创建的触发器名字。
(2) 参数BEFORE和AFTER指定触发器执行的时间。BEFORE指在触发时间之前执行触发语句;AFTER表示在触发时间之后执行触发语句。
(3) 触发时间参数指数据库操作触发条件,其中包括INSERT、UPDATE和DELETE。
(4) 表名指定触发时间操作表的名称。
(5) FOR EACH ROW表示任何一条记录上的操作满足触发事件都会触发该触发器。
(6) 执行语句指触发器被触发后执行的程序。
例14.1下面创建一个由插入命令INSERT触发的触发器auto_save_time,具体步骤如下。(实例位置:光盘\TM\sl\14\14.1****)
(1) 创建一个名称为timelog的表格,该表的结构非常简单。相关代码如下所示。
create table timelog(
id int(11) primary key auto_increment not null,
savetime varchar(50) not null
);
(2) 创建名称为auto_save_time的触发器,其代码如下。
delimiter //
create trigger auto_save_time before insert
on studentinfo for each row
insert into timelog(savetime) values(now());
//
以上代码的运行结果如图14.1所示。
auto_save_time触发器创建成功,其具体的功能是当用户向studentinfo表中执行INSERT操作时,数据库系统会自动在插入语句执行之前向timelog表中插入当前时间。下面通过向studentinfo表中插入一条信息来查看触发器的作用,其代码如下所示。
insert into studentinfo(name) values ('Chris');
然后执行SELECT语句查看timelog表中是否执行INSERT操作,其结果如图14.2所示。

图14.1 创建auto_save_time触发器

图14.2 查看timelog表中是否执行插入操作
以上结果显示,在向studentinfo表中插入数据时,savetime表中也会被插入一条当前系统时间的数据。
14.1.2 创建具有多条执行语句的触发器
14.1.1节中已经介绍了如何创建一个最基本的触发器,但是在实际应用中,往往触发器中包含多条执行语句。其中创建具有多条执行语句的触发器语法结构如下。
CREATE TRIGGER 触发器名称 BEFORE | AFTER 触发事件
ON 表名 FOR EACH ROW
BEGIN
执行语句列表
END
其中,创建具有多条执行语句触发器的语法结构与创建触发器的一般语法结构大体相同,其参数说明请参考14.1.1节中的参数说明,这里不再赘述。在该结构中,将要执行的多条语句放入BEGIN与END之间。多条语句需要执行的内容,需要用分隔符“;”隔开。

例14.2创建一个由DELETE触发多条执行语句的触发器delete_time_info。模拟一个删除日志数据表和一个删除时间表。当用户删除数据库中的某条记录后,数据库系统会自动向日志表中写入日志信息。创建具有多个执行语句的触发器的过程如下:
在例14.1中创建的timelog数据表基础上,另外创建一个名称为timeinfo的数据表,代码如下。(实例位置:光盘\TM\sl\14\14.2****)
create table timeinfo(
id int(11) primary key auto_increment,
info varchar(50) not null
)//
然后创建一个由DELETE触发多条执行语句的触发器delete_time_info,其代码如下。
delimiter //
create trigger delete_time_info after delete
on studentinfo for each row
begin
insert into timelog(savetime) values (now());
insert into timeinfo(info) values ('deleteact');
end
//
运行以上代码的结果如图14.3所示。

图14.3 创建具有多个执行语句的触发器delete_time_info
例14.3触发器创建成功,当执行删除操作后,timelog与timeinfo表中将会插入两条相关记录。执行删除操作的代码如下。(实例位置:光盘\TM\sl\14\14.3****)
DELETE FROM studentinfo where sid=7;
删除成功后,应用SELECT语句分别查看数据表timelog与数据表timeinfo,其运行结果如图14.4和图14.5所示。

图14.4 查看数据表timelog信息

图14.5 查看数据表timeinfo信息
从以上运行结果中可以看出,触发器创建成功后,当用户对students表执行DELETE操作时,students数据库中的timelog数据表和timeinfo数据表中分别被插入操作时间和操作信息。

14.2 查看触发器
查看触发器是指查看数据库中已存在的触发器的定义、状态和语法等信息。查看触发器应用SHOW TRIGGERS语句。
14.2.1 SHOW TRIGGERS
在MySQL中,可以执行SHOW TRIGGERS语句查看触发器的基本信息,其基本形式如下。
SHOW TRIGGERS;
进入MySQL数据库,选择students数据库并查看该数据库中存在的触发器,其运行结果如图14.6所示

图14.6 查看触发器
在命令提示符中输入SHOW TRIGGERS语句即可查看选择数据库中的所有触发器,但是,应用该查看语句存在一定弊端,即只能查询所有触发器的内容,并不能指定查看某个触发器的信息。这样一来,就会在用户查找指定触发器信息的时候带来极大不便。故推荐读者只在触发器数量较少的情况下应用SHOW TRIGGERS语句查询触发器基本信息。
14.2.2 查看triggers表中触发器信息
在MySQL中,所有触发器的定义都存在该数据库的triggers表中。读者可以通过查询triggers表来查看数据库中所有触发器的详细信息。查询语句如下所示。
SELECT * FROM information_schema.triggers;
其中,information_schema是MySQL中默认存在的库,而information_schema是数据库中用于记录触发器信息的数据表。通过SELECT语句查看触发器信息。其运行结果与图14.6相同。但是如果用户想要查看某个指定触发器的内容,可以通过WHERE子句应用TRIGGER字段作为查询条件。其代码如下所示。
SELECT * FROM information_schema.triggers WHERE TRIGGER_NAME='触发器名称';
其中,“触发器名称”这一参数为用户指定要查看的触发器名称,和其他SELECT查询语句相同,该名称内容需要用一对“''”(单引号)引用指定的文字内容。
14.3 使用触发器
在MySQL中,触发器按以下顺序执行:BEFORE触发器、表操作、AFTER触发器操作,其中表操作包括常用的数据库操作命令(如INSERT、UPDATE、DELE TE)。
例14.4触发器与表操作存在执行顺序,下面通过创建一个示例向读者展示三者的执行顺序关系。(实例位置:光盘\TM\sl\14\14.4****)
(1) 创建名称为before_in的BEFORE INSERT触发器,其代码如下。
create trigger before_in before insert on
studentinfo for each row
insert into timeinfo (info) values ('before');
(2) 创建名称为after_in的AFTER INSERT触发器,其代码如下。
create trigger after_in after insert on
studentinfo for each row
insert into timeinfo (info) values ('after');
运行步骤(1)、(2)的结果如图14.7所示。

图14.7 创建触发器运行结果
(3) 创建完毕触发器,向数据表studentinfo中插入一条记录,其代码如下。
insert into studentinfo(name) values ('Nowitzki');
执行成功后,通过SELECT语句查看数据表timeinfo的插入情况,其代码如下。
select * from timeinfo;
运行以上代码,其运行结果如图14.8所示。

图14.8 查看timeinfo表中触发器的执行顺序
查询结果显示BEFORE和AFTER触发器被激活。BEFORE触发器首先被激活,然后AFTER触发器再被激活。

14.4 删除触发器
在MySQL中,既然可以创建触发器,同样也可以通过命令删除触发器。删除触发器指删除原来已经在某个数据库中创建的触发器,与MySQL中删除数据库的命令相似,应用DROP关键字删除触发器。其语法格式如下。
DROP TRIGGER 触发器名称
“触发器名称”参数为用户指定要删除的触发器名称,如果指定某个特定触发器名称,MySQL在执行过程中将会在当前库中查找触发器。

例14.5下面将名称为delete_time_info的触发器删除,其执行代码如下。(实例位置:光盘\TM\sl\14\14.5****)
DROP TRIGGER delete_time_info;
执行上述代码,其运行结果如图14.9所示。

图14.9 删除触发器
通过查看触发器命令来查看数据库students中的触发器信息,其代码如下。
SHOW TRIGGERS
查看触发器信息,可以从图14.10看出,名称为delete_time_info的触发器已经被删除。

图14.10 查看students数据库中的触发器信息

14.5 小结
本章对MySQL数据库的触发器的定义和作用、创建触发器、查看触发器、使用触发器和删除触发器等内容进行了详细讲解,创建触发器和使用触发器是本章的重点内容。读者在创建触发器后,一定要查看触发器的结构。使用触发器时,触发器执行的顺序为BEFORE触发器、表操作(INSERT、UPDATE和DELETE)和AFTER触发器。读者需要将本章的知识结合实际需要来设计触发器。
14.6 实践与练习
1.创建一个由INSERT触发的触发器,实现当向department表中插入数据时,自动向tb_students表中插入当前时间。(答案位置:光盘\TM\sl\14\14.6****)
2.删除原有的触发器,触发器删除完成后,执行SELECT语句来查看触发器是否还存在。(答案位置:光盘\TM\sl\14\14.7****)
第15章 事务的应用
在操作MySQL过程中,对于一般简单的业务逻辑或中小型程序而言,无须考虑应用MySQL事务。但在比较复杂的情况下,往往在执行某些数据操作过程中,需要通过一组SQL语句执行多项并行业务逻辑或程序,这样,就必须保证所用命令执行的同步性,使执行序列中产生依靠关系的动作能够同时操作成功或同时返回初始状态。在此情况下,就需要优先考虑使用MySQL事务处理。
通过阅读本章,读者可以:
l 了解MySQL事务的概述
l 了解MySQL事务的创建与存在周期
l 掌握MySQL事务的查询和提交(回滚)
l 掌握MySQL事务行为
l 掌握MySQL事务的性能
l 掌握应用MySQL伪事务的方法
15.1 MySQL事务概述
在MySQL中,事务由单独单元的一条或多条SQL语句组成。在这个单元中,每条MySQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中一旦某条SQL语句执行失败或产生错误,整个单元将会回滚,所有受到影响的数据将返回到事务开始以前的状态;如果单元中的所有SQL语句均执行成功,则事务被顺利执行。
在现实生活中,事务处理数据的应用非常广泛,如网上交易、银行事务等。下面通过网上交易流程向读者展示事务的概念。

图15.1 应用事务处理网上交易流程
相信大多数用户都有过网上购物的体验,即用户登录某个大型购物网站,浏览该网站中所陈列的商品信息,将喜欢的商品放入购物车中,选购完毕后,用户需要对选购的商品进行在线支付,当用户对所选商品付款完毕,通知商家发货,在此过对选购的商品进行在线支付,当用户对所选商品付款完毕,通知商家发货,在此过程中。用户所付货款并未提交到商户手中,当用户收到货物之后,可以确认收货,商家才收到商品货款,整个交易过程才算完成。如果任何一步操作失败,则都会导致双方陷入尴尬的境界,试想当用户选购商品,付款操作完成后,用户选择在发货过程中取消订单。这时商家并没有得到货款将取消操作,如果不应用事务处理,则用户在取消订单操作过程后,商家仍然继续将用户所订购的商品发送给用户,这会导致一些不愉快的争端。故在整个交易过程中,必须采用事务来对网上交易进行回滚操作。其流程如图15.1所示。
在网上交易流程过程中,商家与用户的交易可以被认为是一个事务处理过程,如果在交易流程中存在一个环节失败,都可能导致双方交易的失败。如前面事务定义所说,所有这些流程都应该被成功执行,在MySQL中如果有任何命令失败,都会导致所有操作命令被撤销。系统返回未操作前的状态,即回滚到初始状态。添加到购物车、在线付款、商家发货等构成了一个基本的事务。整个交易流程可以被看作一个完整的单元,用于实现整体事务。
通过InnoDB和BDB类型表,MySQL事务能够完全满足事务安全的ACID测试。但是并不是所有表类型都支持事务,如MyISAM类型表就不能支持事务,只能通过伪事务对表实现事务处理。

15.1.1 原子性
这就类似与化学中的原子,事务也具备整体性和不可分割性,被认为是一个不可分割的单元。假设一个事务由多种任务组成,其中的语句必须同时操作成功,才可以认为事务是成功的,否则将回滚到初始状态。从上面网上交易的例子可以看出,所有操作的成功是保证交易完成的前提条件,当任何一个环节出现问题时,就导致事务回滚,不能正常完成交易过程。即所有事务共同进退,保证了事务的整体性,这就是事务的原子性。
原子的执行是一个全部发生或全部失败的整体过程。在一个原子操作中,如果事务中的任何一条语句失败,前面执行的语句都将被返回,以保证数据的整体性不被破坏。这在常用的系统应用中,为保证数据的安全性起到一定作用。
15.1.2 一致性
在MySQL事务处理过程中,无论事务是完全成功或是在中途因某些环节失败而导致失败,但事务使系统处于一致的状态时,其必须保证一致性。如在网上进行转账操作的过程中,用户A向用户B的账户中转入5 000元,但用户B在查询转账信息的时候,发现自己的账户中只增加了3 000元,这样不能使整个事务达到一致性。例如,从上述网上交易的例子考虑,当交易完成后,商家的货物会减少,不可能出现当商家给用户发货后货物数量不变,而用户收到货物不增加的情况。
在MySQL中,一致性主要由MySQL的日志机制处理,它记录数据库的所有变化,为事务回复提供跟踪记录。如果系统在事务处理中间发生错误,MySQL恢复过程将使用这些日志发现事务是否已经完全成功执行或需要返回。一致性属性保证数据库从不返回一个未处理的事务。
15.1.3 孤立性
孤立性是指每个事务在自己的空间发生,与其他发生在系统中的事务隔离,而且事务的结果只在它完全被执行时才能看到。即使这样的一个系统中同时发生多个事务,孤立性也可以保证特定的事务在完成之前,其结果是不被公布的。
当系统支持多个同时存在的用户和连接时,系统必须遵守孤立性原则,否则在执行过程中可能导致大量数据被破坏,孤立性保证每个事务完整地在其各自的空间内被顺利执行,保证事务与事务之间不会相互冲突。
15.1.4 持久性
在MySQL中,即便是数据库系统崩溃,一个被提交的事务仍然在坚持。当一个事务完成,数据库的日志已经被更新时,持久性即可发挥其特有功效。在MySQL中,如果系统崩溃或者数据存储介质被破坏,通过使用日志,系统能够恢复在重启前进行的最后一次成功更新,可以反映系统崩溃时处于执行过程的事务的变化。
MySQL的持久性是通过一条记录事务过程中系统变化的二进制事务日志文件来实现的。如果遇到硬件损坏或者系统的异常关机,系统在下一次启动时,通过使用最后的备份和日志就可以恢复丢失数据。

15.2 MySQL事务的创建与存在周期
通过上述对事务定义的叙述和事务特性的讲解,相信读者已经对事务有一个初步的认识。下面的内容将对事务的存在周期做详细讲解。首先,向读者展示在MySQL中如何创建事务。
创建事务的一般过程是:初始化事务、创建事务、应用SELECT语句查询数据是否被录入和提交事务。如果用户不在操作数据库完成后执行事务提交,则系统会默认执行回滚操作。如果用户在提交事务前选择撤销事务,则用户在撤销前的所有事务将被取消,数据库系统会回到初始状态。

在创建事务的过程中,用户需要创建一个InnoDB或BDB类型的数据表,其基本命令结构如下。
CREATE TABLE table_name(field_defintions) TYPE = INNODB/BDB;
其中,table_name为表名,而field_defintions为表内定义的字段等属性,TYPE指定数据表的类型,既可以是InnoDB类型,也可以是BDB类型。
当用户希望将已经存在的表支持事务处理,则用户可以应用ALTER TABLE命令,指定数据表的类型即可实现对表的类型更改操作,使原本不支持事务的数据表更改为支持事务处理的类型,其命令如下。
ALTER TABLE table_name TYPE= INNODB/BDB;
在用户更改完表的类型后,即可使数据表支持事务处理。

15.2.1 初始化事务
初始化MySQL事务,首先声明初始化MySQL事务后所有的SQL语句为一个单元。在MySQL中,应用START TRANSACTION命令来标记一个事务的开始,初始化事务的结构如下。
START TRANSACTION;
另外,用户也可以使用BEGIN或者BEGIN WORK命令初始化事务,通常STAR T TRANSACTION命令后面跟随的是组成事务的SQL语句。
在命令提示符中输入如下命令。
start transaction;
如果在用户输入以上代码后,MySQL数据库没有给出警告提示或返回错误信息,则说明用户已经事务初始化成功,用户可以继续执行下一步操作。
15.2.2 创建事务
例15.1初始化事务成功后,可以创建事务。这里以向名称为connection的数据表中插入一条记录为例,讲解事务的创建。首先打开数据库,选定某个数据库,然后初始化事务,最后创建事务,向指定的数据表中添加记录,其代码如下。(实例位置:光盘\TM\sl\15\15.1****)
mysql –uroot –proot
use db_database15 ;
start transaction;
insert into connection(email,cellphone,QQ,sid)
values('barrystephen@115.com',12456000000,187034000,3);
其运行结果如图15.2所示。

图15.2 创建事务
15.2.3 应用SELECT语句查询数据是否被正确录入
事务创建成功后,建议读者通过SELECT语句查询数据是否被正确录入。
例15.2在事务初始化成功后,用户创建事务,继续在命令提示符中输入如下指令。(实例位置:光盘\TM\sl\15\15.2****)
SELECT * FROM connection WHERE sid=3;
其运行结果如图15.3所示。

图15.3 查询数据是否被正确录入

15.2.4 提交事务
在用户没有提交事务之前,当其他用户连接MySQL服务器时,应用SELECT语句查询结果,则不会显示没有提交的事务。当且仅当用户成功提交事务后,其他用户才可能通过SELECT语句查询事务结果。由事务的特性可知,事务具有孤立性,当事务处在处理过程中,其实MySQL并未将结果写入磁盘中,这样一来,这些正在处理的事务相对其他用户是不可见的。一旦数据被正确插入,用户可以使用COMMIT命令提交事务。提交事务的命令结构如下。
COMMIT
一旦当前执行事务的用户提交当前事务,则其他用户就可以通过会话查询结果。
15.2.5 撤销事务(事务回滚)
撤销事务,又被称作事务回滚,即事务被用户开启、用户输入的SQL语句被执行后,如果用户想要撤销刚才的数据库操作,可使用ROLLBACK命令撤销数据库中的所有变化。ROLLBACK命令结构如下。
ROLLBACK
输入回滚操作后,如何判断是否执行回滚操作了呢?可以通过SELECT语句查看15.2.2节中插入的数据是否存在,其运行结果如图15.4所示。

图15.4 执行回滚操作后应用SELECT查询


15.2.6 事务的存在周期
事务的周期由用户在命令提示符中输入START TRANSACTION指令开始,直至用户输入COMMIT结束,图15.5展示了一个简单事务存在周期流程图。

图15.5 事务的存在周期

15.3 MySQL事务行为
在MySQL中,存在两个可以控制行为的变量,它们分别是AUTOCOMMIT变量和TRANSACTION ISOLACTION LEVEL变量。
15.3.1 自动提交
在MySQL中,如果不更改其自动提交变量,则系统会自动向数据库提交结果,用户在执行数据库操作过程中,不需要使用START TRANSACTION语句开始事务,应用COMMIT或者ROLLBACK提交事务或执行回滚操作。如果用户希望通过控制MySQL自动提交参数,可以更改提交模式,这一更改过程是通过设置AUTOCOMMIT变量来实现的。
下面通过一个示例向读者展示如何关闭自动提交参数,在命令提示符中输入以下命令。
SET AUTOCOMMIT=0 ;
关闭自动提交功能。只有当用户输入COMMIT命令后,MySQL才将数据表中的资料提交到数据库中,如果不提交事务,而终止MySQL会话,数据库将会自动执行回滚操作。
例15.3在关闭自动提交命令后,查询数据表中数据,并且向数据表中添加一条记录,其命令如下。(实例位置:光盘\TM\sl\15\15.3****)
select * from timeinfo;
insert into timeinfo(info) values('test autocommit');
以上代码的运行结果如图15.6所示。

图15.6 取消自动提交操作
由于用户已经关闭自动提交功能,所以图15.6中所示的添加操作由于没有执行事务的提交操作,导致数据没有成功添加。再次查询数据表中的数据,运行结果如图15.7所示。
结果表明,之前插入的数据并未插入到数据库中,另外,可以通过查看@@AUTOCOMMIT变量来查看当前自动提交状态,查看此变量同样应用SELECT语句,其运行结果如图15.8所示。

图15.7 应用SELECT语句查询自动提交关闭后的数据

图15.8 查看自动提交变量
15.3.2 事务的孤立级
事务具有独立的空间,在MySQL服务器中,用户通过不同的会话执行不同的事务,在多用户环境中,许多RDBMS会话在任意指定时刻都是活动的。为了使这些事务互不影响,保证数据库性能不受到影响,采用事务的孤立级是十分有必要的。
孤立级在整个事务中起到了很重要的作用,如果没有这些事务的孤立性,不同的SELECT语句将会在同一事务的环境中检索到不同的结果,这将导致数据的不一致性。给不同的用户造成困扰,这样一来,用户就不能将查询的结果集作为计算基础。所以孤立性强制保持每个事务的独立性,以此来保证事务中看到一致的数据。
基于ANSI/ISO SQL规范,MySQL提供以下4种孤立级。
(1) SERIALIZABLE(序列化):顾名思义,以序列的形式对事务进行处理,该孤立级的特点是只有当事务提交后,用户才能从数据库中查看数据的变化。该孤立级运行会影响MySQL的性能。因为需要占用大量资源,以保证使大量事务在任意时间不被用户看到。
(2) REPEATABLE READ(可重读):对于应用程序的安全性作出部分妥协,以提高其性能。事务在该孤立级上不会被看成一个序列,不过当前在执行事务的过程中,用户仍然看不到事务的过程。直到事务提交为止,用户才能够看到事务的过程中,用户仍然看不到事务的过程。直到事务提交为止,用户才能够看到事务的变化结果。
(3) READ COMMITTED(提交后读):提交后读孤立级的安全性比重复读安全性要低。在这一级的事务,用户可以看到其他事务添加的新记录。在事务处理时,如果存在其他用户同时对事务的相应表进行修改,那么在同一事务中不同时间内,应用SELECT语句可能返回不同的结果集。
(4) READ UNCOMMITTED(未提交读):该孤立级提供事务之间的最小程度间隔,该孤立级容易产生虚幻读操作。其他用户可以在该孤立级上看到未提交的事务。
15.3.3 修改事务的孤立级
在MySQL中,可以使用TRANSACTION ISOLATION LEVEL变量修改事务孤立级,其中,MySQL的默认孤立级为REPEATABLE READ(可重读),用户可以使用SELECT命令获取当前事务孤立级变量的值,其命令如下。
SELECT @@tx_isolation ;
例15.4如果用户希望修改事务的孤立级,可以通过SET命令设置其不同值来修改事务的孤立级,操作命令如图15.9所示。(实例位置:光盘\TM\sl\15\15.4****)

图15.9 设置事务孤立级

15.4 事务的性能
从15.3节中可以看出,应用不同孤立级的事务可能会对系统造成一系列影响,采用不同孤立级处理事务,可能会对系统稳定性和安全性等诸多因素造成影响。另外,有些数据库操作中,不需要应用事务处理,则用户在选择数据表类型时,需要选择合适的数据表类型。所以,在选择表类型时,应该考虑数据表具有完善的功能,且高效执行的前提下,也不会对系统增加额外的负担。
15.4.1 应用小事务
应用小事务的意义在于:保证每个事务不会在执行前等待很长时间,从而避免各个事务因为互相等待而导致系统性能的大幅度下降。用户在应用少数大事务的候,可能无法看出因事务间互相等待而导致系统性能下降,但是当系统中存在处理量很大的数据库或多种复杂事务的时候,用户就可以明显感觉到事务因长时间等待,而导致系统性能下降。所以,应用小事务可以保证系统的性能,其可以快速变化或退出,这样,其他在队列中准备就绪的事务就不会受到明显影响。
15.4.2 选择合适的孤立级
因为事务的性能与其对服务器产生的负载成反比,即当事务孤立级越高,其性能越低,但是其安全性也越高。其性能和孤立级关系如图15.10所示。所以只有选择适当的孤立级,才能有效地提高MySQL系统性能和应用性。

图15.10 事务孤立级性能关系
从图15.10可以看出,虽然随着孤立级的增高稳定性也会随之改变,但是并不代表孤立级的稳定性越高,也不能表明其灵活性越高,故用户在选择孤立级的时候,需要根据自身实际情况选择适合应用的孤立级,切勿生搬硬套。
15.4.3 死锁的概念与避免
死锁,即当两个或者多个处于不同序列的用户打算同时更新某相同的数据库时,因互相等待对方释放权限而导致双方一直处于等待状态。在实际应用中,两个不同序列的客户打算同时对数据执行操作,极有可能产生死锁。更具体地讲,当两个事务相互等待操作对方释放所持有的资源,而导致两个事务都无法操作对方持有的资源,这样无限期的等待被称作死锁。
不过,MySQL的InnoDB表处理程序具有检查死锁这一功能,如果该处理程序发现用户在操作过程中产生死锁,该处理程序立刻通过撤销操作来撤销其中一个事务,以便使死锁消失。这样就可以使另一个事务获取对方所占有的资源而执行逻辑操作。
15.5 MySQL伪事务
在MySQL中,InnoDB和BDB类型表可以支持事务处理,但是MySQL中MyISAM类型表并不能支持事务处理,对于某些应用该类型的数据表,用户可以选择应用表锁定来替代事务。这种引用表锁定来替代事务的事件被称作伪事务。使用表锁来锁定表的操作,可以加强非事务表在执行过程中的安全性和稳定性。
15.5.1 用表锁定代替事务
在MySQL的MyISAM类型数据表中,并不支持COMMIT(提交)和ROLLBACK(回滚)命令。当用户对数据库执行插入、删除、更新等操作时,这些变化的数据都被立刻保存在磁盘中。这样,在多用户环境中,会导致诸多问题,为了避免同一时间有多个用户对数据库中指定表进行操作。可以应用表锁定来避免在用户操作数据表过程中受到干扰。当且仅当该用户释放表的操作锁定后,其他用户才可以访问这些修改后的数据表。
设置表锁定代替事务基本步骤如下。
(1) 为指定数据表添加锁定,其语法如下。
LOCK TABLES table_name lock_type,…
其中,table_name为被锁定的表名,lock_type为锁定类型,该类型包括以读方式(READ)锁定表;以写方式(WRITE)锁定表。
(2) 用户执行数据表的操作,可以添加、删除或者更改部分数据。
(3) 用户完成对锁定数据表的操作后,需要对该表进行解锁操作,释放该表的锁定状态,其语法如下。
UNLOCK TABLES
下面通过实例向读者展示如何以读方式锁定数据表和以写方式锁定数据表。
1. 以读方式锁定数据表
以读方式锁定数据表,该方式是设置锁定用户的其他方式操作,如删除、插入、更新都不被允许,直至用户进行解锁操作。
例15.5演示以读方式锁定表后,向该表中插入数据,以及解锁后再插入数据的情况。(实例位置:光盘\TM\sl\15\15.5****)
首先,以锁定数据表studentinfo为例,在命令提示符下输入如下代码。
lock table studentinfo read;
然后,应用SELECT语句查看表中的信息,其运行结果如图15.11所示。

图15.11 查看以读方式锁定的studentinfo表
下面尝试向该数据表中插入一条数据,其运行结果如图15.11所示。

图15.11 向以读方式锁定的表中插入数据
从上述结果可以看出,当用户试图向数据库插入数据时,将会返回失败信息。当用户将锁定的表解锁后,再次执行插入操作,其运行结果如图15.12所示。

图15.12 向解锁后的数据表中添加数据
锁定被释放后,用户可以对数据库执行添加、删除、更新等操作。

2. 以写方式锁定数据表
与读方式锁定表类似,表的写锁定是设置用户可以修改数据表中的数据,但是除自己以外其他会话中的用户不能进行任何读操作。在命令提示符中输入如下命令。
lock table studentinfo write;
例15.6因为该表为写锁定,用户可以对数据库的数据执行修改、添加、删除等操作。在该命令提示符中应用SELECT语句查询该锁定表,其运行结果如图15.14所示。(实例位置:光盘\TM\sl\15\15.6****)

图15.14 查询应用写操作锁定的数据表studentinfo
从图15.14中,读者可以看到,当前用户应用SELECT语句仍然可以查询该表的数据,并没有限制用户对数据表的读操作。这是因为,以写方式锁定数据表并不能限制当前锁定用户的查询操作,下面打开一个新用户会话,即保持如图15.14所示窗口不被关闭,重新打开一个新的MySQL连接,并执行上述过程。其运行结果如图15.14所示。

图15.14 打开新会话查询被锁定的数据表
在新打开的命令提示界面,读者可以看到,应用SELECT语句执行查询操作,并没有结果显示,这是因为之前该表以写方式锁定。故当操作用户释放该数据表锁定后,其他用户才可以通过SELECT语句查看之前被锁定的数据表。在命令提示符中输入如下代码。
UNLOCK TABLES;
这时,在第二次打开的命令提示符中,即可出现如图15.14所示的结果。当数据表被释放锁定后,其他访问数据库的用户即可查看数据表的内容。

15.5.2 应用表锁实现伪事务
相信读者通过上面的学习,已经了解什么是表锁。下面的示例通过使用表锁对MyISAM表进行锁定操作,以此过程来代替事务型表InnoDB,即应用表锁来实现伪事务。实现伪事务的一般步骤如下。
(1) 对数据库中的数据表进行锁定操作,可以对多个表做不同的方式锁定,其代码格式如下。
LOCK TABLE table_name1 lock_type1, table_name2 lock_type2,…
(2) 执行数据库操作,向锁定的数据表中执行添加、删除、修改操等操作。
如前面提到的INSERT、UPDATE、DELETE等操作。用户可以对锁定的数据表执行上述操作,在执行过程中,该伪事务所产生的结果是不会被其他用户更改的。
(3) 释放锁定的数据表,以便让正在队列中等待查看或操作的其他用户可以浏览数据表中的数据或对操作表执行各种数据的操作。
如果存在其他会话要求访问已锁定的多个表格,则该会话必须被迫等待当前锁定用户释放锁定表,才允许其他会话访问该数据表,表锁定使不同会话执行的数据库操作彼此独立。应用数据表锁定方式可以使不支持事务类型的表实现伪事务。
15.6 小结
本章对MySQL中对事务的创建、提交、撤销,以及存在周期进行了详细讲解,并通过举例说明,帮助读者更好地理解所学知识的用法。在阅读本章时,读者应该重点掌握事务如何自动提交,并且修改事务的孤立级。同时读者应该了解MySQL事务和性能,以及MySQL伪事务。
15.7 实践与练习
1.在PHP中使用事务处理技术实现银行的安全转账。(答案位置:光盘\TM\sl\15\15.7****)
2.在Java中实现模拟银行转账系统,通过事务保证转账业务的顺利进行。(答案位置:光盘\TM\sl\15\15.8****)
第16章 事件
在系统管理或者数据库管理中,经常要周期性地执行某一个命令或者SQL语句。MySQL在5.1以后推出了事件调度器(Event Scheduler),可以很方便地实现MySQL数据库的计划任务,定期运行指定命令,使用起来非常简单和方便。
通过阅读本章,读者可以:
l 了解查看事件是否开启的方法
l 掌握开启事件的方法
l 掌握使用创建事件语句创建事件的方法
l 掌握使用修改事件语句修改事件,以及临时关闭事件的方法
l 了解删除事件的方法
16.1 事件概述
在MySQL 5.1中新增了一个特色功能事件调度器(Event Scheduler),简称事件。它可以作为定时任务调度器,取代部分原来只能用操作系统的计划任务才能执行的工作。另外,更值得一提的是,MySQL的事件可以实现每秒钟执行一个任务,这在一些对实时性要求较高的环境下是非常实用的。
事件调试器是定时触发执行的,从这个角度上看也可以称作是“临时触发器”。但是它与触发器又有所区别,触发器只针对某个表产生的事件执行一些语句,而事件调度器则是在某一段(间隔)时间执行一些语句。
16.1.1 查看事件是否开启
事件由一个特定的线程来管理。启用事件调度器后,拥有SUPER权限的账户执行SHOW PROCESSLIST命令就可以看到这个线程了。
例16.1查看事件是否开启,具体代码如下。(实例位置:光盘\TM\sl\16\16.1****)
SHOW VARIABLES LIKE 'event_scheduler';
SELECT @@event_scheduler;
SHOW PROCESSLIST;
运行以上代码的结果如图16.1所示。

图16.1 查看事件是否开启
从图16.1中可以看出事件没有开启,因为参数event_scheduler的值为OFF,并且在PROCESSLIST中查看不到event_scheduler的信息;而如果参数event_scheduler的值为ON,或者在PROCESSLIST中显示了event_scheduler的信息,那么就说明事件已经开启。
16.1.2 开启事件
通过设定全局变量event_scheduler的值即可动态地控制事件调度器是否启用。开启MySQL的事件调度器,可以通过下面两种方式实现。
1. 通过设置全局参数修改
在MySQL的命令行窗口中,使用SET GLOBAL命令可以开启或关闭事件。将event_scheduler参数的值设置为ON,则开启事件;如果设置为OFF,则关闭事件。例如,要开启事件可以在命令行窗口中输入下面的命令。
SET GLOBAL event_scheduler = ON;
例16.2开启事件并查看事件是否已经开启,具体代码如下。(实例位置:光盘\TM\sl\16\16.2****)
SET GLOBAL event_scheduler = ON;
SHOW VARIABLES LIKE 'event_scheduler';
运行以上代码的结果如图16.2所示。

图16.2 开启事件并查看事件是否已经开启
从图16.2中,可以看出event_scheduler的值为ON,则表示事件已经开启。

2. 更改配置文件
在MySQL的配置文件my.ini(Windows系统)/my.cnf(Linux系统)中,找到[mysqld],然后在下面添加以下代码开启事件。
event_scheduler=ON
在配置文件中添加代码并保存文件后,还需要重新启动MySQL服务器才能生效。通过该方法开启事件,重启MySQL服务器后,不恢复为系统默认的未开启状态。例如,此时重新连接MySQL服务器,然后使用下面的命令查看事件是否开启时,得到的结果将是参数event_scheduler的值为ON,表示已经开启,如图16.3所示。

图16.3 查看事件是否开启
16.2 创建事件
在MySQL 5.1以上版本中,可以通过CREATE EVENT语句来创建事件,其语句格式如下。
CREATE [DEFINER = { user | CURRENT_USER }]
EVENT [IF NOT EXISTS] event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO event_body;
从上面的语法中,可以看出CREATE EVENT语句由多个子句组成,各子句的详细说明如表16.1所示。
表16.1 CREATE EVENT语句的子句
子 句 | 说 明 |
---|---|
DEFINER | 可选,用于定义事件执行时要检查权限的用户 |
IF NOT EXISTSS | 可选,用于判断要创建的事件是否存在 |
EVENT event_name | 必须,用于指定事件名,event_name的最大长度为64个字符,如果未指定event_name,则默认为当前的MySQL用户名(不区分大小写) |
ON SCHOEDULE schedule | 必选,用于定义执行的时间和时间间隔 |
ON COMPLETION[NOT]PRESERVE | 可选,用于定义事件是否循环执行,即是一次执行还是永久执行,默认为一次执行,即NOT PRESERVE |
ENABLE[DISABLE]DISABLE ON SLAVE | 可选,用于指定事件的一种属性。其中,关键字ENABLE表示该事件是活动的,也就是调度器检查事件是否必须调用;关键字DISABLE表示该事件是关闭的,也就是事件的声明存储到目录中,但是调度器不会检查它是否应该调用;关键字DISABLE ON SLAVE表示事件在从机中是关闭的。如果不指定这三个选项中的任何一个,则在一个时间创建之后,它立即变为活动的。 |
COMMENT‘comment’ | 可选,用于定义事件的注释 |
DO event_body | 必选,用于指定事件启动时所要执行的代码,可以是任何有效地SQL语句、存储过程或者一个计划执行的事件。若果包含多条语句,可以使用BEGIM…END符合结构 |
在ON SCHEDULE子句中,参数schedule的值为一个AS子句,用于指定事件在某个时刻发生,其语法格式如下。
AT timestamp [+ INTERVAL interval] ...
| EVERY interval
[STARTS timestamp [+ INTERVAL interval] ...]
[ENDS timestamp [+ INTERVAL interval] ...]
参数说明如下。
(1)timestamp:表示一个具体的时间点,后面加上一个时间间隔,表示在这个时间间隔后事件发生。
(2)EVERY子句:用于表示事件在指定时间区间内每隔多长时间发生一次,其中STARTS子句用于指定开始时间;ENDS子句用于指定结束时间。
(3)interval:表示一个从现在开始的时间,其值由一个数值和单位构成。例如,使用“4 WEEK”表示4周;使用“'1:10' HOUR_MINUTE”表示1小时10分钟。间隔的距离用DATE_ADD()函数来支配。
interval参数值的语法格式如下。
quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE
WEEK | SECOND | YEAR_MONTH | DAY_HOUR
DAY_MINUTE |DAY_SECOND | HOUR_MINUTE
HOUR_SECOND | MINUTE_SECOND}
例16.3在数据库db_database16中创建一个名称为e_test的事件,用于每隔5秒钟向数据表tb_eventtest中插入一条数据。(实例位置:光盘\TM\sl\16\16.3****)
(1)打开数据库db_database16,代码如下。
use db_database16;
(2)创建名称为e_test的事件,用于每隔5秒钟向数据表tb_eventtest中插入一条数据,代码如下。
CREATE EVENT IF NOT EXISTS e_test ON SCHEDULE EVERY 5 SECOND
ON COMPLETION PRESERVE
DO INSERT INTO tb_eventtest(user,createtime) VALUES('root',NOW());
(3)创建事件后,编写以下查看数据表tb_eventtest中数据的代码。
select * from tb_eventtest;
执行结果如图16.4所示,从该图中可以看出,每隔5秒钟插入一条数据,这说明事件已经创建成功。

图16.4 创建事件e_test
例16.4创建一个事件,实现每个月的第一天凌晨1点统计一次已经注册的会员人数,并插入到统计表中。(实例位置:光盘\TM\sl\16\16.4****)
(1)创建名称为p_total的存储过程,用于统计已经注册的会员人数,并插入到统计表tb_total中,具体代码如下。
DELIMITER //
create procedure p_total()
begin
DECLARE n_total INT default 0;
select COUNT(*) into n_total FROM db_database16.tb_user;
INSERT INTO tb_total (userNumber,createtime) values(n_total,NOW());
end
//
(2)创建名称为e_autoTotal的事件,用于在每个月的第一天凌晨1点调用步骤(1)中创建的存储过程p_total,代码如下。
CREATE EVENT IF NOT EXISTS e_autoTotal
ON SCHEDULE EVERY 1 MONTH
STARTS DATE_ADD(DATE_ADD(DATE_SUB(CURDATE(),INTERVAL DAY(CURDATE())-1 DAY),
INTERVAL 1 MONTH),INTERVAL 1 HOUR)
ON COMPLETION PRESERVE ENABLE
DO CALL p_total();
创建存储过程的执行结果如图16.5所示;创建事件的执行结果如图16.6所示。

图16.5 创建存储过程p_total

图16.6 创建事件e_autoTotal
16.3 修改事件
在MySQL 5.1及以后版本中,事件被创建之后,还可以使用ALTER EVENT语句修改其定义和相关属性,其语法格式如下。
ALTER
[DEFINER = { user | CURRENT_USER }]
EVENT event_name
[ON SCHEDULE schedule]
[ON COMPLETION [NOT] PRESERVE]
[RENAME TO new_event_name]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
[DO event_body]
ALTER EVENT语句的使用语法与CREATE EVENT语句基本相同,这里不再赘述。另外,ALTER EVENT语句还有一个用法就是让一个事件关闭或再次让其活动。不过需要注意的是,一个事件最后一次被调用后,它是无法被修改的,因为此时它已经不存在了。
例16.5修改例16.3中创建的事件,让其每隔30秒向数据表tb_eventtest中插入一条数据。(实例位置:光盘\TM\sl\16\16.5****)
(1)在MySQL的命令行窗口中,编写修改事件的代码,具体代码如下。
ALTER EVENT e_test ON SCHEDULE EVERY 30 SECOND
ON COMPLETION PRESERVE
DO INSERT INTO tb_eventtest(user,createtime) VALUES('root',NOW());
(2)编写查询数据表中数据的代码,具体代码如下。
SELECT * FROM tb_eventtest;
执行结果如图16.7所示。

图16.7 修改名称为e_test的事件

应用ALTER EVENT语句,还可以临时关闭一个已经创建的事件,下面将举例进行说明。
例16.6临时关闭例16.3中创建的事件e_test。(实例位置:光盘\TM\sl\16\16.6****)
(1)在MySQL的命令行窗口中,编写临时关闭事件e_test的代码,具体代码如下。
ALTER EVENT e_test DISABLE;
(2)编写查询数据表中数据的代码,具体代码如下。
SELECT * FROM tb_eventtest;
为了查看事件是否关闭,可以执行两次(每次间隔1分钟)步骤(2)中的代码,执行结果如图16.8所示。

图16.8 临时关闭名称为e_test的事件

16.4 删除事件
在MySQL 5.1及以后版本中,删除已经创建的事件可以使用DROP EVENT语句来实现。DROP EVENT语句的语法格式如下。
DROP EVENT [IF EXISTS] event_name
例16.7删除例16.3中创建的事件e_test,代码如下。(实例位置:光盘\TM\sl\16\16.7****)
use db_database16
DROP EVENT IF EXISTS e_test;
执行结果如图16.9所示。

图16.9 删除名称为e_test的事件
16.5 小结
本章首先介绍了什么是事件、如何查看事件是否开启,以及如何开启事件;然后介绍了如何创建、修改和删除事件。其中,如何开启、创建、修改和删除事件是本章的重点,需要读者认真学习并做到融会贯通,为以后的工作和学习打下良好的基础。
16.6 实践与练习
1.创建事件,实现每隔一个月清空一次新闻信息表。(答案位置:光盘\TM\sl\16\16.8****)
2.在数据库db_database16中创建一个名称为e_test的事件,用于每隔1分钟向数据表tb_eventtest中插入一条数据。(答案位置:光盘\TM\sl\16\16.9****)
第17章 备份与恢复
为了保证数据的安全,需要定期对数据进行备份。备份的方式有很多种,效果也不一样。如果数据库中的数据出现了错误,就需要使用备份好的数据进行数据还原。这样可以将损失降至最低。而且,可能还会涉及数据库之间的数据导入与导出。本章将介绍备份和还原的方法,对MySQL数据库的数据安全等内容进行讲解。
通过阅读本章,读者可以:
l 掌握数据备份的使用方法
l 掌握数据恢复的方法
l 掌握数据库迁移的方法
l 掌握导出和导入文本文件的方法
17.1 数据备份
备份数据是数据库管理最常用的操作。为了保证数据库中数据的安全,数据管理员需要定期进行数据备份。一旦数据库遭到破坏,即通过备份的文件来还原数据库。
可能造成数据损坏的原因很多,大体上可以归纳为以下几个方面。
(1)存储介质故障:保存数据库文件的磁盘设备损坏,同时又没有对数据库备份,从而导致数据彻底丢失。
(2)服务器彻底瘫痪:数据库服务器彻底瘫痪,系统需要重建。
(3)用户的误操作:在删除数据库时,不小心删除了某些重要数据,或者是整个数据库。
(4)黑客破坏:系统遭到黑客的恶意攻击,数据或者数据表被删除。
因此,数据备份是很重要的工作。本节将介绍数据备份的方法。
17.1.1 使用mysqldump命令备份
mysqldump命令可以将数据库中的数据备份成一个文本文件。表的结构和表中的数据将存储在生成的文本文件中。本节将介绍mysqldump命令的工作原理和使用方法。
mysqldump命令的工作原理很简单。它先查出需要备份的表的结构,再在文本文件中生成一条CREATE语句。然后,将表中的所有记录转换成一条INSERT语句。这些CREATE语句和INSERT语句都是还原时使用的。还原数据时就可以使用其中的CREATE语句来创建表。使用其中的INSERT语句来还原数据。
在使用mysqldump命令进行数据备份时,经常分为以下3种形式。
(1)备份一个数据库。
(2)备份多个数据库。
(3)备份所有数据库。
下面将分别介绍如何实现这3种形式的数据备份。
1. 备份一个数据库
使用mysqldump命令备份一个数据库的基本语法如下。
mysqldump –u username -p dbname table1 table2 …>BackupName.sql
其中,dbname参数表示数据库的名称;table1和table2参数表示表的名称,没有该参数时将备份整个数据库;BackupName.sql参数表示备份文件的名称,文件名前面可以加上一个绝对路径。通常将数据库备份成一个后缀名为.sql的文件。

例17.1下面使用root用户备份test数据库下的student表,命令如下。(实例位置:光盘\TM\sl\17\17.1****)
mysqldump –u root –p test student >D:\ student.sql
在DOS命令窗口中执行上面的命令时,将提示输入连接数据库的密码,输入密码后将完成数据备份,这时可以在D:\中找到student.sql文件。student.sql文件中的部分内容如图17.1所示。

图17.1 备份一个数据库
文件开头记录了MySQL的版本、备份的主机名和数据库名。文件中,以“--”开头的都是SQL的注释。以“/!40101”等形式开头的内容是只有MySQL版本大于或等于指定的版本4.1.1才执行的语句。下面的“/!40103”“/*!40014”也是这个作用。

2.备份多个数据库
mysqldump命令备份多个数据库的语法如下。
mysqldump –u username –p --databases dbname1 dbname2 >BackupName.sql
这里要加上databases这个选项,然后后面跟多个数据库的名称。
例17.2下面使用root用户备份test数据库和mysql数据库,命令如下。(实例位置:光盘\TM\sl\17\17.2****)
mysqldump –u root -p --databases test mysql >D:\backup.sql
在DOS命令窗口中执行上面的命令时,将提示输入连接数据库的密码,输入密码后将完成数据备份,这时可以在D:\下面看到名为backup.sql的文件,如图17.2所示。这个文件中存储着这两个数据库的所有信息。

图17.2 备份多个数据库
3.备份所有数据库
mysqldump命令备份所有数据库的语法如下。
mysqldump –u username –p --all –databases >BackupName.sql
使用--all –databases选项就可以备份所有数据库了。
例17.3下面使用root用户备份所有数据库。命令如下。(实例位置:光盘\TM\sl\17\17.3****)
mysqldump –u root -p --all -databases >D:\all.sql
在DOS命令窗口中执行上面的命令时,将提示输入连接数据库的密码,输入密码后将完成数据备份,这时可以在D:\下面看到名为all.sql的文件,如图17.3所示。这个文件存储着所有数据库的所有信息。

图17.3 备份所有数据库
17.1.2 直接复制整个数据库目录
MySQL有一种最简单的备份方法,就是将MySQL中的数据库文件直接复制出来。这种方法最简单,速度也最快。使用这种方法时,最好将服务器先停止。这样,可以保证在复制期间数据库中的数据不会发生变化。如果在复制数据库的过程中还有数据写入,就会造成数据不一致。

这种方法虽然简单快捷,但不是最好的备份方法。因为,实际情况可能不允许停止MySQL服务器。而且,这种方法对InnoDB存储引擎的表不适用。对于MyISAM存储引擎的表,这样备份和还原很方便。但是还原时最好是相同版本的MySQL数据库,否则可能会存储文件类型不同的情况。

17.1.3 使用mysqlhotcopy工具快速备份
如果备份时不能停止MySQL服务器,可以采用mysqlhotcopy工具。mysqlhotcopy工具的备份方式比mysqldump命令快。下面介绍mysqlhotcopy工具的工作原理和使用方法。
mysqlhotcopy工具是一个Perl脚本,主要在Linux操作系统下使用。mysqlhotcopy工具使用LOCK TABLES、FLUSH TABLES和cp来进行快速备份。其工作原理是,先将需要备份的数据库加上一个读操作锁,然后,用FLUSH TABLES将内存中的数据写回到硬盘上的数据库中,最后,把需要备份的数据库文件复制到目标目录。使用mysqlhotcopy的命令如下。
[root@localhost ~]#mysqlhotcopy[option] dbname1 dbname2…backupDir/
其中,dbname1等表示需要备份的数据库的名称;backupDir参数指出备份到哪个文件夹下。这个命令的含义就是将dbname1、dbname2等数据库备份到backDir目录下。mysqlhotcopy工具有一些常用的选项,这些选项的介绍如下。
(1)--help:用来查看mysqlhotcopy的帮助。
(2)--allowold:如果备份目录下存在相同的备份文件,将旧的备份文件名加上_old。
(3)--keepold:如果备份目录下存在相同的备份文件,不删除旧的备份文件,而是将旧文件更名。
(4)--flushlog:本次备份之后,将对数据库的更新记录到日志中。
(5)--noindices:只备份数据文件,不备份索引文件。
(6)--user=用户名:用来指定用户名,可以用-u代替。
(7)--password=密码:用来指定密码,可以用-p代替。使用-p时,密码与-p紧挨着。或者只使用-p,然后用交换的方式输入密码。这与登录数据库时的情况是一样的。
(8)--port=端口号:用来指定访问端口,可以用-P代替。
(9)--socket=socket文件:用来指定socket文件,可以用-S代替。

17.2 数据恢复
管理员的非法操作和计算机的故障都会破坏数据库文件。当数据库遇到这些意外时,可以通过备份文件将数据库还原到备份时的状态。这样可以将损失降低到最小。本节将介绍数据还原的方法。
17.2.1 使用mysql命令还原
通常使用mysqldump命令将数据库的数据备份成一个文本文件。通常这个文件的后缀名是.sql。需要还原时,可以使用mysql命令来还原备份的数据。
备份文件中通常包含CREATE语句和INSERT语句。mysql命令可以执行备份文件中的CREATE语句和INSERT语句。通过CREATE语句来创建数据库和表。通过INSERT语句来插入备份的数据。mysql命令的基本语法如下。
mysql –uroot –p [dbname] <backup.sql
其中,dbname参数表示数据库名称。该参数是可选参数,可以指定数据库名,也可以不指定。指定数据库名时,表示还原该数据库下的表。不指定数据库名时,表示还原特定的一个数据库。备份文件中有创建数据库的语句。
例17.4下面使用root用户备份所用数据库,命令如下。(实例位置:光盘\TM\sl\17\17.4****)
mysql –u root –p <D:\all.sql
在DOS命令窗口中执行上面的命令时,将提示输入连接数据库的密码,输入密码后将完成数据还原。这时,MySQL数据库就已经还原了all.sql文件中的所有数据库。

17.2.2 直接复制到数据库目录
之前介绍过一种直接复制数据的备份方法。通过这种方式备份的数据,可以直接复制到MySQL的数据库目录下。通过这种方式还原时,必须保证两个MySQL数据库的主版本号是相同的。而且,这种方式对MyISAM类型的表比较有效。对于InnoDB类型的表则不可用。因为InnoDB表的表空间不能直接复制。
在Windows操作系统下,MySQL的数据库目录通常存放在下面3个路径的其中之一,分别是C:\mysql\date、C:\Documents and Settings\All Users\Application Data\MySQL\MySQL Server5.1\data或者C:\Program Files\MySQL Server 5.1\data。在Linux操作系统下,数据库目录通常在/var/lib/mysql/、/usr/local/mysql/data或者/usr/local/mysql/var这3个目录下。上述位置只是数据库目录最常用的位置。具体位置根据读者安装时设置的位置而定。
使用mysqlhotcopy命令备份的数据也是通过这种方式来还原的。在Linux操作系统下,复制到数据库目录后,一定要将数据库的用户和组变成mysql,命令如下。
chown –R mysql.mysql dataDir
其中,两个mysql分别表示组和用户;-R参数可以改变文件夹下的所有子文件的用户和组;dataDir参数表示数据库目录。

17.3 数据库迁移
数据库迁移就是指将数据库从一个系统移动到另一个系统上。数据库迁移的原因是多种多样的。可能是因为升级了计算机,或者是部署开发的管理系统,或者是升级了MySQL数据库,甚至是换用其他的数据库。根据上述情况,可以将数据库迁移大致分为3类,分别是在相同版本的MySQL数据库之间迁移、迁移到其他版本的MySQL数据库中和迁移到其他类型的数据库中。本节将介绍数据库迁移的方法。
17.3.1 相同版本的MySQL数据库之间的迁移
相同版本的MySQL数据库之间的迁移就是在主版本号相同的MySQL数据库之间进行数据库移动。这种迁移的方式最容易实现。本节将介绍这方面的内容。
相同版本的MySQL数据库之间进行数据库迁移的原因很多。通常的原因是换了新的机器,或者是装了新的操作系统。还有一种常见的原因就是将开发的管理系统部署到工作机器上。因为迁移前后MySQL数据库的主本版号相同,所以可以通过复制数据库目录来实现数据库迁移。但是,只有数据库表都是MyISAM类型的才能使用这种方式。
最常用和最安全的方式是使用mysqldump命令来备份数据库。然后使用mysql命令将备份文件还原到新的MySQL数据库中。这里可以将备份和迁移同时进行。假设从一个名为host1的机器中备份出所有数据库,然后,将这些数据库迁移到名为host2的机器上。命令如下。
mysqldump –h name1 –u root –password=password1 –all-databases
mysql –h host2 –u root –password=password2
其中,“|”符号表示管道,其作用是将mysqldump备份的文件送给mysql命令;“-password=password1”是name1主机上root用户的密码;同理,password2是name2主机上的root用户的密码。通过这种方式可以直接实现迁移。
17.3.2 不同数据库之间的迁移
不同数据库之间迁移是指从其他类型的数据库迁移到MySQL数据库,或者从MySQL数据库迁移到其他类型的数据库。例如,某个网站原来使用Oracle数据库,因为运营成本太高等诸多原因,希望改用MySQL数据库;或者,某个管理系统原来使用MySQL数据库,因为某种特殊性能的要求,希望改用Oracle数据库。这样的不同数据库之间的迁移也经常会发生,但是这种迁移没有普通适用的解决方法。
MySQL以外的数据库也有类似mysqldump这样的备份工具,可以将数据库中的文件备份成.sql文件或普通文件。但是,因为不同数据库厂商没有完全按照SQL标准来设计数据库,这就造成了不同数据库使用的SQL语句的差异。例如,微软的SQL Server软件使用的是T-SQL。T-SQL中包含非标准的SQL语句。这就造成了SQL Server和MySQL的SQL语句不能兼容。
除了SQL语句存在不兼容的情况下,不同的数据库之间的数据类型也有差异。数据类型的差异也造成了迁移的困难。例如,SQL Server数据库中有ntext、Image等数据类型,而MySQL数据库都没有;MySQL支持的ENUM和SET类型,这些SQL Server数据库不支持。从某种意义上说,这种差异是商业数据库公司故意造成的壁垒,是阻碍数据库市场健康发展的。
17.4 表的导出和导入
MySQL数据库中的表可以导出成文本文件、XML文件或者HTML文件,相应的文本文件也可以导入MySQL数据库中。在数据库的日常维护中,经常需要进行表的导出和导入的操作。本节将介绍将表内容导出和导入文本文件的方法。
17.4.1 用SELECT ...INTO OUTFILE导出文本文件
MySQL中,可以在命令行窗口(MySQL Commend Line Client)中使用SELECT…INTO OUTFILE语句将表的内容导出成一个文本文件。其基本语法形式如下。
SELECT[列名] FROM table[WHERE语句]
INTO OUTFILE '目标文件'[OPTION];
该语句分为两个部分。前半部分是一个普通的SELECT语句,通过这个SELECT语句来查询所需要的数据;后半部分是导出数据的。其中,“目标文件”参数指出将查询的记录导出到哪个文件;OPTION参数可以有常用的几个选项。介绍如下。
(1)FIELDS TERMINATED BY'字符串':设置字符串为字段的分隔符,默认值是“\t”。
(2)FIELDS ENCLOSED BY'字符':设置字符来括上字段的值。默认情况下不使用任何符号。
(3)FIELDS OPTIOINALLY ENCLOSED BY'字符':设置字符来括上CHAR、VARCHAR和TEXT等字符型字段。默认情况下不使用任何符号。
(4)FIELDS ESCAPED BY'字符':设置转义字符,默认值为“\”。
(5)LINES STARTING BY'字符串':设置每行开头的字符,默认情况下无任何字符。
(6)LINES TERMINATED BY'字符串':设置每行的结束符,默认值是“\n”。
例17.5下面用SELECT…INTO OUTFILE语句来导出test数据库下order表的记录。其中,字段之间用“、”隔开,字符型数据用双引号括起来,每条记录以“>”开头。命令如下。(实例位置:光盘\TM\sl\17\17.5****)
SELECT * FROM test.order INTO OUTFILE 'D:\order.txt'
FIELDS TERMINATED BY '\、' OPTIONALLY ENCLOSED BY '"'
LINES STARTING BY '>' TERMINATED BY '\r\n';
“TERMINATED BY '\r\n'”可以保证每条记录占一行。因为Windows操作系统下“\r\n”才是回车换行。如果不加这个选项,默认情况只是“\n”。用root用户登录到MySQL数据库中,然后执行上述命令。执行完后,可以在D:\下看到一个名为order.txt的文本文件。order.txt中的内容如图17.4所示。

图17.4 用SELECT…INTO OUTFILE导出文本文件
这些记录都是以“>”开头,每个字段之间以“、”隔开。而且,字符数据都加上了引号。
17.4.2 用mysqldump命令导出文本文件
mysqldump命令可以备份数据库中的数据。但是,备份时是在备份文件中保存了CREATE语句和INSERT语句。不仅如此,mysqldump命令还可以导出文本文件。其基本的语法形式如下。
mysqldump –u root –pPassword –T 目标目录 dbname table [option];
其中,Password参数表示root用户的密码,密码紧挨着-p选项;目标目录参数指出文本文件的路径;dbname参数表示数据库的名称;table参数表示表的名称;option表示附件选项。这些选项介绍如下。
(1)--fields-terminated-by=字符串:设置字符串为字段的分隔符,默认值是“\t”。
(2)--fields-enclosed-by=字符:设置字符来括上字段的值。
(3)--fields-optionally-enclosed-by=字符:设置字符括上CHAR、VARCHAR和TEXT等字符型字段。
(4)--fields-escaped-by=字符:设置转义字符。
(5)--lines-terminated-by=字符串:设置每行的结束符。

例17.6用mysqldump语句来导出test数据库下order表的记录。其中,字段之间用“、”隔开,字符型数据用双引号括起来。命令如下。(实例位置:光盘\TM\sl\17\17.6****)
mysqldump -u root -p -T D:\ test order "--lines-terminated-by=\r\n"
"--fields-terminated-by=、" "--fields-optionally-enclosed-by=""
其中,root用户的密码为111,密码紧挨着-p选项。--fields-terminated-by等选项都用双引号括起来。命令执行完后,可以在D:\下看到一个名为order.txt的文本文件和order.sql文件。order.txt中的内容如图17.5所示。

图17.5 用MYSQLDUMP命令导出文本文件
这些记录都是以“、”隔开,而且,字符数据都是加上了引号。其实,mysqldump命令也是调用SELECT…INTO OUTFILE语句来导出文本文件的;同时mysqldump命令还生成了student.sql文件。这个文件中有表的结构和表中的记录。

mysqldump命令还可以导出XML格式的文件,其基本语法如下。
mysqldump-u root –pPassword --xml|–X dbname table >D:\name.xml;
其中,Password表示root用户的密码;使用--xml或者-X选项就可以导出XML格式的文件;dbname表示数据库的名称;table表示表的名称;D:\name.xml表示导出的XML文件的路径。
例17.7使用mysqldump命令将数据表student中的内容导出到XML文件中。效果如图17.6所示。(实例位置:光盘\TM\sl\17\17.7****)

图17.6 在DOS命令窗口中的执行效果
生成的XML文件可以在D盘的根目录下找到,内容如图17.7所示。

图17.7 生成的XML文件
17.4.3 用mysql命令导出文本文件
mysql命令可以用来登录MySQL服务器,也可以用来还原备份文件。同时,mysql命令也可以导出文本文件。其基本语法形式如下。
mysql –u root –pPassword –e "SELECT 语句" dbname >D:/name.txt;
其中,Password表示root用户的密码;使用-e选项就可以执行SQL语句:“SELECT语句”用来查询记录;D:/name.txt表示导出文件的路径。
例17.8下面用mysql命令来导出test数据库下student表的记录,命令如下。(实例位置:光盘\TM\sl\17\17.8****)
mysql -u root -p111 -e"SELECT * FROM student" test > D:/student2.txt
在DOS命令窗口中执行上述命令,可以将student表中的所用记录查询出来,然后写入到student2.txt文档中。student2.txt被保存在D盘根目录下,如图17.8所示。
student2.txt中的内容如图17.9所示。

图17.8 mysql命令导出文本文件

图17.9 文档内容
mysql命令还可以导出XML文件和HTML文件。mysql命令导出XML文件的语法如下。
mysql –u root –pPassword --xml|–X –e "SELECT 语句" dbname >D:/filename.xml
其中,Password表示root用户的密码;使用--xml或者-X选项就可以导出XML格式的文件;dbname表示数据库的名称;D:/name.xml表示导出的XML文件的路径。
例如,下面的命令可以将test数据库中的student表的数据导出到名称为student.xml的XML文件中。
mysql -u root -p111 --xml -e "SELECT * from student" test >D:/ student.xml
mysql命令导出HTML文件的语法如下。
mysql –u root –pPassword --html|–H –e "SELECT 语句" dbname >D:/filename.html
其中,使用--html或者-H选项就可以导出HTML格式的文件。
例如,下面的命令可以将test数据库中的student表的数据导出到名称为student.html的HTML文件中。
mysql -u root -p111 --html -e "SELECT * from student" test >D:/student.html
17.4.4 用LOAD DATA INFILE命令将文本文件导入到数据表
在MySQL中,可以通过命令LOAD DATA INFILE来实现将指定格式的文本文件导入到数据表中。LOAD DATA INFILE命令的语法格式如下。
LOAD DATA [LOW_PRIORITY|CONCURRENT] [LOCAL] INFILE file_name INTO TABLE table_name
[OPTION];
参数说明如下。
(1)LOW_PRIORITY:如果指定LOW_PRIORITY,则LOAD DATA语句会被延迟,直到没有其他的客户端正在读取表。
(2)CONCURRENT:如果指定CONCURRENT,则当LOAD DATA正在执行时,其他线程可以同时使用该表的数据。
(3)LOCAL:如果指定了LOCAL,则文件会被客户主机上的客户端读取,并被发送到服务器。文件会被给予一个完整的路径名称,以指定其确切的位置。如果给定的是一个相对路径名称,则此名称会被理解为相对于启动客户端时所在的目录。如果没有指定LOCAL,则文件必须位于服务器主机上,并且被服务器直接读取。使用LOCAL的速度会略慢些。
(4)file_name:用来指定要导入的文本文件的路径和名称。这个文件可以手动创建,也可以使用其他的程序创建。可以使用绝对路径(如D:/student1.txt),也可以不指定路径,直接写上文件名(如student1.txt),这时服务器将在默认数据库目录中查找并读取。
(5)table_name用来指定需要导入数据的表名,该表在数据库中必须存在,表结构必须与导入文件的数据一致。
(6)OPTION用于设置相应的选项,其值可以是下面9个值中的任何一个。
① FIELDS TERMINATED BY '字符串':用于设置字段的隔符为字符串对象,默认值为“\t”。
② FIELDS ENCLOSED BY '字符':用于设置括上字段值的字符符号,默认情况下不使用任何符号。
③ FIELDS OPTIONALLY ENCLOSED BY字符:用来设置括上CHAR、VARCHAR和TEXT等字段值的字符符号,默认情况下不使用任何符号。
④ FIELDS ESCAPED BY '':用于设置转义字符的字符符号,默认情况下使用“\”字符。
⑤ LINES STARTING BY '字符':用来设置每行开头的字符符号,默认情况下不使用任何符号。
⑥ LINES TERMINATED BY '字符串':用于设置每行结束的字符串符号,默认情况下使用“\n”字符串。
⑦ IGNORE n LINES:用于指定忽略文件的前n行记录。
⑧(字段列表):用于实现根据字段列表中的字符和顺序来加载记录。
⑨ SET column=expr:用于设置列的转换条件,即所指定的列经过相应转换后才会被加载。

例17.9使用LOAD DATA INFILE命令,将D盘根目录下的student1.txt文件中的数据记录导入到数据库test中的student1表中。(实例位置:光盘\TM\sl\17\17.9****)
(1)准备一个名称为student1.txt的文本文件,并放置在D盘根目录下,内容如图17.10所示,对于这个文件,可以使用例17.8介绍的方法来进行导出。
(2)进入到MySQL的命令行窗口中,输入以下命令选择数据库test。
USE test;
执行效果如图17.11所示。

图17.10 student1.txt的内容

图17.11 选择数据库
(3)在test数据库中,创建一张名称为student1的数据表,表结构如图17.11所示。

图17.11 数据表student1的表结构
(4)执行LOAD DATA INFILE命令,将文本文件student1.txt中的数据导入到表student1中,具体代码如下。
LOAD DATA INFILE 'D:/student1.txt' INTO TABLE student1
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES;
执行效果如图17.12所示,数据表student1中的数据如图17.14所示。

图17.12 执行LOAD DATA INFILE命令导入文本文件到数据表
(5)应用SELECT语句查询数据表student1中的数据,代码如下。
select * from student1;
执行结果如图17.14所示。

图17.14 数据表student1的数据
17.4.5 用mysqlimport命令导入文本文件
在MySQL中,如果只是恢复数据表中的数据,可以在Windows的命令提示符窗口中使用mysqllimport命令来实现。通过mysqlimport命令可以实现将指定格式的文本文件导入到数据表中。实际上,这个命令提供了LOAD DATA INFILE语句的一个命令行接口,它发送一个LOAD DATA INFILE命令到服务器来运行,它的大多数选项直接对应于LOAD DATA INFILE命令。mysqlimport命令的语法格式如下。
mysqlimport --no-defaults –u root –pPassword –T database file_name [option];

其中,--no-defaults表示指定不要从任何选项文件中读取默认选项,这是必选项;Password参数表示root用户的的密码,密码紧挨着-p选项;目标目录参数指出文本文件的路径;database参数表示要导入数据的数据库的名称;file_name参数表示要导入数据的文本文件名;OPTION用于设置相应的选项,其值可以是下面几个值中的任何一个。
(1)--fields-terminated-by=字符串:用于设置字段的分隔符,默认值是“\t”。
(2)--fields-enclosed-by=字符:用于设置括上字段值的字符符号,默认情况下不使用任何符号。
(3)--fields-optionally-enclosed-by=字符:用于设置括上CHAR、VARCHAR和TEXT等字符型字段值的字符符号,默认情况下不使用任何符号。
(4)--fields-escaped-by=字符:用于设置转义字符。
(5)--lines-terminated-by=字符串:用于设置每行的结束符。
(6)--ignore-lines=n:用于指定忽略文件的前n行记录。
例17.10使用mysqlimport命令,将D盘根目录下的student1.txt文件中的数据记录导入到数据库test中的student1表中。(实例位置:光盘\TM\sl\17\17.10****)
(1)准备一个名称为order.txt的文本文件,并放置在D盘根目录下,内容如图17.14所示。
(2)在test数据库中,创建一张名称为order的数据表,表结构如图17.15所示。

图17.14 order.txt的内容

图17.15 数据表order的表结构
(3)执行mysqlimport命令,将文本文件order.txt中的数据导入到表order中,具体代码如下。
mysqlimport --no-defaults -u root -p111 test D:\order.txt "--lines-terminated-by=\r\n" "--fields-terminated-by=\t"
"--fields-optionally-enclosed-by=""
执行效果如图17.17所示。

图17.17 执行mysqlimport命令导入文本文件到数据表
(4)应用SELECT语句查询数据表order中的数据,代码如下。
use test;
select * from order
;
执行结果如图17.17所示。

图17.17 数据表order的数据

17.5 小结
本章对备份数据库、还原数据库、数据库迁移、导出表和导入表进行了详细讲解,备份数据库和还原数据库是本章的重点内容。在实际应用中,通常使用mysqldump命令备份数据库,使用mysql命令还原数据库。数据库迁移、导出表和导入表是本章的难点。数据迁移需要考虑数据库的兼容性问题,最好是在相同版本的MySQL数据库之间迁移。导出表和导入表的方法比较多,希望读者能够多练习这些方法的使用。
17.6 实践与练习
1.实现将数据表student中的内容导出到文本文件中,在生成文本文件时,每个字段之间用逗号隔开。每个字符型的数据用双引号括起来。而且,每条记录占一行。(答案位置:光盘\TM\sl\17\17.11****)
2.使用mysql命令,将表中的记录导出到HTML文件中。(答案位置:光盘\TM\sl\17\17.11****)
第18章 MySQL性能优化
性能优化是通过某些有效的方法提高MySQL数据库的性能。性能优化的目的是为了使MySQL数据运行速度更快、占用的磁盘空间更小。性能优化包括很多方面,例如优化查询速度、优化更新速度和优化MySQL服务器等。本章将介绍性能优化的目的,优化查询、优化数据库结构和优化多表查询等的方法,以提高MySQL数据库的速度。
通过阅读本章,读者可以:
l 了解优化查询的方法
l 了解优化数据库结构的方法
l 了解查询高速缓存的方法
l 了解优化多表查询的方法
l 了解优化表设计的方法
18.1 优化概述
优化MySQL数据库是数据库管理员的必备技能,通过不同的优化方式达到提高MySQL数据库性能的目的。本节将介绍优化的基本知识。
MySQL数据库的用户和数据非常少的时候,很难判断一个MySQL数据库性能的好坏。只有当长时间运行,并且有大量用户进行频繁操作时,MySQL数据库的性能才能体现出来。例如,一个每天有几万用户同时在线的大型网站的数据库性能的优劣就很明显。这么多用户在同时连接MySQL数据库,并且进行查询、插入和更新的操作,如果MySQL数据库的性能很差,很可能无法承受如此多用户同时操作。试想如果用户查询一条记录需要花费很长时间,用户很难会喜欢这个网站。
因此,为了提高MySQL数据库的性能,需要进行一系列的优化措施。如果MySQL数据库需要进行大量的查询操作,那么就需要对查询语句进行优化。对于耗费时间的查询语句进行优化,可以提高整体的查询速度。如果连接MySQL数据库用户很多,那么就需要对MySQL服务器进行优化;否则,大量的用户同时连接MySQL数据库,可能会造成数据库系统崩溃。
数据库管理员可以使用SHOW STATUS语句查询MySQL数据库的性能。语法形式如下。
SHOW STATUS LIKE 'value';
其中,value参数是常用的几个统计参数,具体介绍如下。
(1)Connections:连接MySQL服务器的次数。
(2)Uptime: MySQL服务器的上线时间。
(3)Slow_queries:慢查询的次数。
(4)Com_select:查询操作的次数。
(5)Com_insert:插入操作的次数。
(6)Com_delete:删除操作的次数。

如果需要查询MySQL服务器的连接次数,可以执行下面的SHOW STATUS语句。
SHOW STATUS LIKE 'Connections';
通过这些参数可以分析MySQL数据库性能。然后根据分析结果,进行相应的性能优化。
例18.1使用SHOW STATUS语句实现查看MySQL服务器的连接和查询次数。查看MySQL服务器的连接和查询次数的语句执行效果如图18.1所示。(实例位置:光盘\TM\sl\18\18.1****)

图18.1 查看MySQL服务器的连接和查询次数
使用SHOW STATUS语句时,可以通过指定统计参数为Connections、Com_select和Slow_queries,来实现显示MySQL服务器的连接数、查询次数和慢查询次数的功能,关键代码如下。
SHOW STATUS LIKE 'Connections';
SHOW STATUS LIKE 'Com_select';
SHOW STATUS LIKE 'Slow_queries';
18.2 优化查询
查询是数据库最频繁的操作,提高查询速度可以有效地提高MySQL数据库的性能。本节将介绍优化查询的方法。
18.2.1 分析查询语句
分析查询语句在前面章节中都有应用,在MySQL中,可以使用EXPLAIN语句和DESCRIBE语句来分析查询语句。
应用EXPLAIN关键字分析查询语句,其语法结构如下。
EXPLAIN SELECT语句;
“SELECT语句”参数为一般数据库查询命令,如SELECT * FROM students。
例18.2下面使用EXPLAIN语句分析一个查询语句,其代码如下。(实例位置:光盘\TM\sl\18\18.2****)
EXPLAIN SELECT * FROM timeinfo ;
其运行结果如图18.2所示。
图18.2 应用EXPLAIN分析查询语句
其中各字段所代表的意义如下所示。
(1)id列:指出在整个查询中SELECT的位置。
(2)table列:存放所查询的表名。
(3)type列:连接类型,该列中存储很多值,范围从const到ALL。
(4)possible_keys列:指出为了提高查找速度,在MySQL中可以使用的索引。
(5)key列:指出实际使用的键。
(6)rows列:指出MySQL需要在相应表中返回查询结果所检验的行数,为了得到该总行数,MySQL必须扫描处理整个查询,再乘以每个表的行值。
(7)Extra列:包含一些其他信息,设计MySQL如何处理查询。
在MySQL中,也可以应用DESCRIBE语句来分析查询语句。DESCRIBE语句的使用方法与EXPLAIN语法是相同的,这两者的分析结果也大体相同。其中,DESCRIBE的语法结构如下。
DESCRIBE SELECT 语句;
在命令提示符下输入如下命令。
describe select * from studentinfo;
其运行结果如图18.3所示。

图18.3 应用DESCRIBE分析查询语句
将图18.3与图18.2对比,读者可以清楚地看出,其运行结果基本相同。分析查询也可以应用关键字DESCRIBE。

18.2.2 索引对查询速度的影响
在查询过程中使用索引,势必会提高数据库查询效率,应用索引来查询数据库中的内容,可以减少查询的记录数,从而达到查询优化的目的。
例18.3通过对使用索引和不使用索引进行对比,来分析查询的优化情况。(实例位置:光盘\TM\sl\18\18.3****)
首先,分析未使用索引时的查询情况,其代码如下。
explain select * from studentinfo where name= 'mrsoft ';
其运行结果如图18.4所示。

图18.4 未使用索引的查询情况
上述结果表明,表格字段rows下为7,这意味着在执行查询过程中,数据库存在的7条数据都被查询了一遍,这样在数据存储量小的时候,查询不会有太大影响,试想当数据库中存储庞大的数据资料时,用户为了搜索一条数据而遍历整个数据库中的所有记录,将会耗费很多时间。现在,在name字段上建立一个名为index_name的索引。创建索引的代码如下。
CREATE INDEX index_name ON studentinfo(name);
上述代码的作用是在studentinfo表的name字段上添加索引。在建立索引完毕后,然后再应用关键字EXPLAIN分析执行情况,其代码如下所示。
explain select * from studentinfo where name = 'mrsoft ';
其运行结果如图18.5所示。

图18.5 使用索引后查询情况
从上述结果可以看出,由于创建的索引使访问的行数由7行减少到1行,所以,在查询操作中,使用索引不但会自动优化查询效率,同时也会降低服务器的开销。
18.2.3 使用索引查询
在MySQL中,索引可以提高查询的速度,但并不能充分发挥其作用,所以在应用索引查询时,也可以通过关键字或其他方式来对查询进行优化处理。
1.应用关键字LIKE优化索引查询
例18.4下面的示例应用关键字LIKE,并且匹配字符串中含有百分号“%”符号,应用EXPLAIN语句执行如下命令。(实例位置:光盘\TM\sl\18\18.4****)
EXPLAIN SELECT * FROM studentinfo WHERE name LIKE '%l';
其运行结果如图18.6所示。

图18.6 应用关键字LIKE优化索引查询
从图18.6中可能看出其rows参数仍为“7”并没有起到优化作用,这是因为如果匹配字符串中,第一个字符为百分号“%”时,索引不会被使用,如果“%”所在匹配字符串中的位置不是第一位置,则索引会被正常使用,在命令提示符中输入如下命令。
EXPLAIN SELECT * FROM studentinfo WHERE name LIKE 'le%';
运行结果如图18.7所示。

图18.7 正常应用索引的LIKE子句运行结果
2.查询语句中使用多列索引
多列索引在表的多个字段上创建一个索引。只有查询条件中使用了这些字段中的一个字段时,索引才会被正常使用。
应用多列索引在表的多个字段中创建一个索引,其命令如下。
CREATE INDEX index_student_info ON studentinfo(name,sex);

3.查询语句中使用关键字OR
在MySQL中,查询语句只有包含关键字OR时,要求查询的两个字段必须同为索引,如果所搜索的条件中,有一个字段不为索引,则在查询中不会应用索引进行查询。其中,应用关键字OR查询索引的命令如下。
SELECT * FROM studentinfo WHERE name='Chris' or sex='M';
例18.5通过EXPLAIN来分析查询命令,在命令提示符中输入如下代码。(实例位置:光盘\TM\sl\18\18.5****)
EXPLAIN SELECT * FROM studentinfo WHERE name='Chris’ or sex='M';
其运行结果如图18.8所示。

图18.8 应用关键字OR
从图18.8中可以看出,由于两个字段均为索引,故查询被优化。如果在子查询中存在没有被设置成索引的字段,则将该字段作为子查询条件时,查询速度不会被优化。
18.3 优化数据库结构
数据库结构是否合理,需要考虑是否存在冗余、对表的查询和更新的速度、表中字段的数据类型是否合理等多方面的内容。本节将介绍优化数据库结构的方法。
18.3.1 将字段很多的表分解成多个表
有些表在设计时设置了很多的字段。这些表中有些字段的使用频率很低。当这些表的数据量很大时,查询数据的速度就会很慢。本节将介绍优化这种表的方法。
对于这种字段特别多且有些字段的使用频率很低的表,可以将其分解成多个表。
例18.6下面的学生表中有很多字段,其中在extra字段中存储着学生的备注信息。有些备注信息的内容特别多,但是,备注信息很少使用。这样就可以分解出另外一个表,将这个表取名为student_extra。表中存储两个字段,分别为id和extra。其中,id字段为学生的学号,extra字段存储备注信息。student_extra表的结构如表18.9所示。(实例位置:光盘\TM\sl\18\18.6****)

图18.9 将字段很多的表分解成多个表
如果需要查询某个学生的备注信息,可以用学号(id)来查询。如果需要将学生的学籍信息与备注信息同时显示时,可以将student表和student_extra表进行联表查询,查询语句如下。
SELECT * FROM student,student_extra WHERE student.id=student_extra.id;
通过这种分解,可以提高student表的查询效率。因此,遇到这种字段很多,而且有些字段使用不频繁的,可以通过这种分解的方式来优化数据库的性能。
18.3.2 增加中间表
有时需要经常查询某两个表中的几个字段。如果经常进行联表查询,会降低MySQL数据库的查询速度。对于这种情况,可以建立中间表来提高查询速度。本节将介绍增加中间表的方法。
先分析经常需要同时查询哪几个表中的哪些字段,然后将这些字段建立一个中间表,并将原来那几个表的数据插入到中间表中,之后就可以使用中间表来进行查询和统计。
例18.7创建中间表temp_score表保存经常要查询的学号、姓名和成绩等信息。(实例位置:光盘\TM\sl\18\18.7****)
下面有个学生表student和分数表score,这两个表的结构如图18.10所示。

图18.10 增加中间表
实际中经常要查学生的学号、姓名和成绩。根据这种情况可以创建一个temp_score表。temp_score表中存储3个字段,分别是id、name和grade。CREATE语句执行如下。
CREATE TABLE temp_score(id INT NOT NULL,
Name VARCHAR(20) NOT NULL,
grade FLOAT);
然后从student表和score表中将记录导入到temp_score表中。INSERT语句如下。
INSERT INTO temp_score SELECT student.id,student.name,score.grade
FROM student,score WHERE student.id=score.stu_id;
将这些数据插入到temp_score表中以后,可以直接从temp_score表中查询学生的学号、姓名和成绩。这样就省去了每次查询时进行表连接的操作,可以提高数据库的查询速度。
18.3.3 优化插入记录的速度
插入记录时,索引、唯一性校验都会影响到插入记录的速度。而且,一次插入多条记录和多次插入记录所耗费的时间是不一样的。根据这些情况,分别进行不同的优化。本节将介绍优化插入记录的速度的方法。
1.禁用索引
插入记录时,MySQL会根据表的索引对插入的记录进行排序。如果插入大量数据时,这些排序会降低插入记录的速度。为了解决这种情况,在插入记录之前应先禁用索引,等到记录都插入完毕后再开启索引。禁用索引的语句如下。
ALTER TABLE 表名 DISABLE KEYS;
重新开启索引的语句如下。
ALTER TABLE 表名 ENABLE KEYS;
对于新创建的表,可以先不创建索引。等到记录都导入以后再创建索引。这样可以提高导入数据的速度。
2.禁用唯一性检查
插入数据时,MySQL会对插入的记录进行校验。这种校验也会降低插入记录的速度。可以在插入记录之前禁用唯一性检查。等到记录插入完毕后再开启。禁用唯一性检查的语句如下。
SET UNIQUE_CHECKS=0;
重新开启唯一性检查的语句如下。
SET UNIQUE_CHECKS=1;
3.优化INSERT语句
插入多条记录时,可以采取两种写INSERT语句的方式。第一种是一个INSERT语句插入多条记录。INSERT语句的情形如下。
INSERT INTO food VALUES
(NULL,'果冻', 'CC果冻厂',1.8, '2011', '北京'),
(NULL, '咖啡', 'CF咖啡厂',25, '2011', '天津'),
(NULL, '奶糖', '旺仔奶糖',14, '2012', '广东');
第二种是一个INSERT语句只插入一条记录,执行多个INSERT语句来插入多条记录。INSERT语句的情形如下。
INSERT INTO food VALUES(NULL, '果冻', 'CC果冻厂',1.8, '2011', '北京');
INSERT INTO food VALUES(NULL, '咖啡', 'CF咖啡厂',25, '2011', '天津');
INSERT INTO food VALUES(NULL, '奶糖', '旺仔奶糖',14, '2012', '广东');
第一种方式减少了与数据库之间的连接等操作,其速度比第二种方式要快。
18.3.4 分析表、检查表和优化表
分析表主要作用是分析关键字的分布。检查表主要作用是检查表是否存在错误。优化表主要作用是消除删除或者更新造成的空间浪费。本节将介绍分析表、检查表和优化表的方法。
1.分析表
MySQL中使用ANALYZE TABLE语句来分析表,该语句的基本语法如下。
ANALYZE TABLE 表名1[,表名2…];
使用ANALYZE TABLE分析表的过程中,数据库系统会对表加一个只读锁。在分析期间,只能读取表中的记录,不能更新和插入记录。ANALYZE TABLE语句能够分析InnoDB和MyISAM类型的表。
例18.8下面使用ANALYZE TABLE语句分析score表,分析结果如图18.11所示。(实例位置:光盘\TM\sl\18\18.8****)

图18.11 分析表
上面结果显示了4列信息,详细介绍如下。
(1)Table:表示表的名称。
(2)Op:表示执行的操作。analyze表示进行分析操作,check表示进行检查查找,optimize表示进行优化操作。
(3)Msg_type:表示信息类型,其显示的值通常是状态、警告、错误或信息。
(4)Msg_text::显示信息。
检查表和优化表之后也会出现这4列信息。
2.检查表
MySQL中使用CHECK TABLE语句来检查表。CHECK TABLE语句能够检查InnoDB和MyISAM类型的表是否存在错误。而且,该语句还可以检查视图是否存在错误。该语句的基本语法如下。
CHECK TABLE 表名1[,表名2….][option];
其中,option参数有5个参数,分别是QUICK、FAST、CHANGED、MEDIUM和EXTENDED。这5个参数的执行效率依次降低。option选项只对MyISAM类型的表有效,对InnoDB类型的表无效。CHECK TABLE语句在执行过程中也会给表加上只读锁。
3.优化表
MySQL中使用OPTIMIZE TABLE语句来优化表。该语句对InnoDB和MyISAM类型的表都有效。但是,OPTILMIZE TABLE与拒绝只能优化表中的VARCHAR、BLOB或TEXT类型的字段。OPTILMIZE TABLE语句的基本语法如下。
OPTIMIZE TABLE 表名1[,表名2…];
通过OPTIMIZE TABLE语句可以消除删除和更新造成的磁盘碎片,从而减少空间的浪费。OPTIMIZE TABLE语句在执行过程中也会给表加上只读锁。

18.4 查询高速缓存
在MySQL中,用户通过SELECT语句查询数据时,该操作将结果集保存到一个特殊的高级缓存中,从而实现查询操作。首次查询后,当用户再次做相同查询操作时,MySQL即可从高速缓存中检索结果。这样一来,既提高了查询速率,也起到优化查询的作用。
18.4.1 检验高速缓存是否开启
例18.9在MySQL中应用关键字VARIABLES,以通配符形式查看服务器变量。其代码如下。(实例位置:光盘\TM\sl\18\18.9****)
SHOW VARIABLES LIKE ' %query_cache %';
运行上述代码,其结果如图18.11所示。

图18.11 检验高速缓存是否开启
下面对主要的参数进行说明。
(1)have_query_cache:表明服务器在默认安装条件下,是否已经配置查询高速缓存。
(2)query_cache_size:高速缓存分配空间,如果该空间为86,则证明分配给高速缓存空间的大小为86MB。如果该值为0,则表明查询高速缓存已经关闭。
(3)query_cache_type:判断高速缓存开启状态,其变量值范围为0~2。其中当该值为0或OFF时,表明查询高速缓存已经关闭;当该值为1或ON时表明高速缓存已经打开;其值为2或DEMAND时,表明要根据需要运行带有SQL_CACHE选项的SELECT语句,提供查询高速缓存。
18.4.2 使用高速缓存
在MySQL中,查询高速缓存的具体语法结构如下。
SELECT SQL_CACHE * FROM 表名 ;
例18.10下面通过具体示例来查询高速缓存运行中的结果。在命令提示符下输入以下命令。(实例位置:光盘\TM\sl\18\18.10****)
SELECT SQL_CACHE * FROM student ;
其运行结果如图18.12所示。

图18.12 使用查询高速缓存运行结果
然后不使用高速缓存查询该数据表,其结果如图18.14所示。

图18.14 未使用查询高速缓存运行结果
如果经常运行查询高速缓存,将会提高MySQL数据库的性能。

18.5 优化多表查询
在MySQL中,用户可以通过连接来实现多表查询,在查询过程中,用户将表中的一个或多个共同字段进行连接,定义查询条件,返回统一的查询结果。这通常用来建立RDBMS常规表之间的关系。在多表查询中,可以应用子查询来优化多表查询,即在SELECT语句中嵌套其他SELECT语句。采用子查询优化多表查询的好处有很多,其中,可以将分步查询的结果整合成一个查询,这样就不需要再执行多个单独查询,从而提高了多表查询的效率。
例18.11下面通过一个实例来说明如何优化多表查询,首先在命令提示符下输入如下命令。(实例位置:光盘\TM\sl\18\18.11****)
select address from student where id=(select id from student_extra where name='nihao');
其运行结果如图18.14所示。

图18.14 应用一般SELECT嵌套子查询
下面应用优化算法,以便可以优化查询速度。在命令提示符下输入以下命令。
select address from student as stu,student_extra as stu_e where stu.id=stu_e.id and stu_e.extra='nihao';
以上命令的作用是将student和student_extra表分别设置别名stu、stu_e,通过两个表的id字段建立连接,并判断student_extra表中是否含有名称为“nihao”的内容,并将地址在屏幕上输出。该语句已经将算法进行优化,以便提高数据库的效率从而实现查询优化的效果。其运行结果如图18.15所示。

图18.15 应用算法的优化查询
如果用户希望避免因出现SELECT嵌套而导致代码可读性下降,则用户可以通过服务器变量来进行优化处理,下面应用SELECT嵌套方式来查询数据,在命令提示符中输入如下命令。
select name from student where age> (select avg(age) from student_extra) ;
其运行结果如图18.17所示。

图18.17 应用SELECT嵌套查询数据
上述合并两个查询的速率将优于子查询运行速率,故采用服务器变量也可以优化查询。
18.6 优化表设计
在MySQL数据库中,为了优化查询,使查询能够更加精炼、高效,在用户设计数据表的同时,也应该考虑一些因素。
首先,在设计数据表时应优先考虑使用特定字段长度,后考虑使用变长字段,如在用户创建数据表时,考虑创建某个字段类型为varchar而设置其字段长度为255,但是在实际应用时,该用户所存储的数据根本达不到该字段所设置的最大长度,命令外如设置用户性别的字段,往往可以用“M”表示男性,“F”表示女性,如果给该字段设置长度为varchar(50),则该字段占用了过多列宽,这样不仅浪费资源,也会降低数据表的查询效率。适当调整列宽不仅可以减少磁盘空间,同时也可以使数据在进行处理时产生的I/O过程减少。将字段长度设置成其可能应用的最大范围可以充分地优化查询效率。
改善性能的另一项技术是使用OPTIMIZE TABLE命令处理用户经常操作的表。频繁地操作数据库中的特定表会导致磁盘碎片的增加,这样会降低MySQL的效率,故可以应用该命令处理经常操作的数据表,以便于优化访问查询效率。
在考虑改善表性能的同时,要检查用户已经建立的数据表,划分数据的优势在于可以使用户更好地设计数据表,但是过多的表意味着性能降低,故用户应检查这些表。检查这些表是否有可能整合为一个表中,如没有必要整合,在查询过程中用户可以使用连接,如果连接的列采用相同的数据类型和长度,同样可以达到查询优化的作用。

18.7 小结
本章对数据库优化的含义和查看数据性能参数的方法进行了详细讲解,然后介绍了优化查询的方法、优化数据库结构的方法和优化MySQL服务器的方法。优化查询的方法和优化数据库结果是本章的重点内容,优化查询部分主要介绍了索引对查询速度的影响。优化数据库结构部分主要介绍了如何对表进行优化。本章的难点是优化MySQL服务器,因为这部分涉及很多MySQL配置文件和配置文件中的参数。
18.8 实践与练习
1.实现在MySQL中使用OPTIMIZE TABLE语句来优化表。(答案位置:光盘\TM\sl\18\18.11****)
2.使用DESCRIBE语句分析一个查询语句。(答案位置:光盘\TM\sl\18\18.12****)
第19章 权限管理及安全控制
保护MySQL数据库的安全,就如同离开汽车时锁上车门,设置警报器。之所以这么做,主要是因为如果不采取这些基本但很有效的防范措施,那么汽车或者是车中的物品被盗的可能性会大大增加。本章将介绍有效保护MySQL数据库安全的一些有效措施。
通过阅读本章,读者可以:
l 了解安全保护策略
l 了解用各种命令实现对MySQL数据库的权限管理的方法
l 掌握使账户密码更安全的方法
l 掌握状态文件和日志文件
19.1 安全保护策略概述
要确保MySQL的安全,看看首先应当做点儿什么?
1.为操作系统和所安装的软件打补丁
如今打开计算机的时候,都会弹出软件的安全警告。虽然有些时候这些警告会给我们带来一些困扰,但是采取措施确保系统打上所有的补丁是绝对有必要的。利用攻击指令和Internet上丰富的工具,即使恶意用户在攻击方面没有多少经验,也可以毫无阻碍地攻击未打补丁的服务器。即使用户在使用托管服务器,也不要过分依赖服务提供商来完成必要的升级;相反,要坚持间隔性手动更新,以确保和补丁相关的事情都被处理妥当。
2.禁用所有不使用的系统服务
始终要注意在将服务器放入网络之前,已经消除所有不必要的潜在服务器攻击途径。这些攻击往往是不安全的系统服务带来的,通常运行在不为系统管理员所知的系统中。简言之,如果不打算使用一个服务,就禁用该服务。
3.关闭端口
虽然关闭未使用的系统服务是减少成功攻击可能性的好方法,不过还可以通过关闭未使用的端口来添加第二层安全。对于专用的数据库服务器,可以考虑关闭除22(SSH协议专用)、3306(MySQL数据库使用的)和一些“工具”专用的(如112(NTP专用))等端口号在1024以下的端口。简言之,如果不希望在指定端口有数据通信,就关闭这个端口。除了在专用防火墙工具或路由器上做这些调整之外,还可以考虑利用操作系统的防火墙。
4.审计服务器的用户账户
特别是当已有的服务器再作为公司的数据库主机时,要确保禁用所有非特权用户,或者最好是全部删除。虽然MySQL用户和操作系统用户完全无关,但他们都要访问服务器环境,仅凭这一点就可能会有意地破坏数据库服务器及其内容。为完全确保在审计中不会有遗漏,可以考虑重新格式化所有相关的驱动器,并重新安装操作系统。
5.设置MySQL的root用户密码
对所有MySQL用户使用密码。客户端程序不需要知道运行它的人员的身份。对于客户/服务器应用程序,用户可以指定客户端程序的用户名。例如,如果other_user没有密码,任何人可以简单地用mysql -u other_user db_name冒充他人调用mysql程序进行连接。如果所有用户账户均存在密码,使用其他用户的账户进行连接将困难得多。
19.2 用户和权限管理
MySQL数据库中的表与其他任何关系表没有区别,都可以通过典型的SQL命令修改其结构和数据。随着版本3.22.11的发行,可以使用GRANT和REVOKE命令。通过这些命令,可以创建和禁用用户,可以在线授予和撤回用户访问权限。由于语法严谨,这消除了由于不好的SQL查询(例如,忘记在UPDATE查询中加入WHERE字句)所带来的潜在危险的错误。
在5.0版本中,开发人员向MySQL管理工具又增加了两个新命令:CREATE USER和DROP USER。从而能更容易地增加新用户、删除和重命名用户,还增加了第三个命令RENAME USER用于重命名现有的用户。
19.2.1 使用CREATE USER命令创建用户
CREATE USER用于创建新的MySQL账户。要使用CREATE USER语句,必须拥有mysql数据库的全局CREATE USER权限,或拥有INSERT权限。对于每个账户,CREATE USER会在没有权限的mysql.user表中创建一个新记录。如果账户已经存在,则出现错误。使用自选的IDENTIFIED BY子句,可以为账户设置一个密码。user值和密码的设置方法和GRANT语句一样。其命令的原型如下所示。
CREATE USER user [IDENTIFIED BY[PASSWORD 'PASSWORD']
[, user [IDENTIFIED BY[PASSWORD 'PASSWORD']]…
例19.1应用CREATE USER命令创建一个新用户,用户名为mrsoft,密码为mr,其运行结果如图19.1所示。(实例位置:光盘\TM\sl\19\19.1****)

图19.1 通过CREATE USER创建mrsoft的用户
19.2.2 使用DROP USER命令删除用户
如果存在一个或是多个账户被闲置,应当考虑将其删除,确保不会用于可能的违法的活动。利用DROP USER命令就能很容易地做到,它将从权限表中删除用户的所有信息,即来自所有授权表的账户权限记录。DROP USER命令原型如下所示。
DROP USER user [, user] ...

例19.2应用DROP USER命令删除用户名为mrsoft的用户,其运行结果如图19.2所示。(实例位置:光盘\TM\sl\19\19.2****)

图19.2 使用DROP USER删除mrsoft的用户
19.2.3 使用RENAME USER命令重命名用户
RENAME USER语句用于对原有MySQL账户进行重命名。RENAME USER语句的命令原型如下。
RENAME USER old_user TO new_user
[, old_user TO new_user] ...

例19.3应用RENAME USER命令将用户名为mrsoft的用户重新命名为lh,其运行结果如图19.3所示。(实例位置:光盘\TM\sl\19\19.3****)

图19.3 使用RENAME USER对mrsoft的用户重命名
19.2.4 GRANT和REVOKE命令
GRANT和REVOKE命令用来管理访问权限,也可以用来创建和删除用户,但在MySQL 5.0.2中可以利用CREATE USER和DROP USER命令更容易地实现这些任务。GRANT和REVOKE命令对于谁可以操作服务器及其内容的各个方面提供了多程度的控制,从谁可以关闭服务器,到谁可以修改特定表字段中的信息都能控制。表19.1中列出了使用这些命令可以授予或撤回的所有权限。
表19.1 GRANT和REVOKE管理权限
如果授权表拥有含有mixed-case数据库或表名称的权限记录,并且lower_case_table_names系统变量已设置,则不能使用REVOKE撤销权限,必须直接操纵授权表(当lower_case_table_names已设置时,GRANT将不会创建此类记录,但是此类记录可能已经在设置变量之前被创建了)。
授予的权限可以分为多个层级。
1.全局层级
全局权限适用于一个给定服务器中的所有数据库,存储在mysql.user表中。GRANT ALL ON .和REVOKE ALL ON .只授予和撤销全局权限。
2.数据库层级
数据库权限适用于一个给定数据库中的所有目标,存储在mysql.db和mysql.host表中。GRANT ALL ON db_name.和REVOKE ALL ON db_name.只授予和撤销数据库权限。
3.表层级
表权限适用于一个给定表中的所有列,存储在mysql.tables_priv表中。GRANT ALL ON db_name.tbl_name和REVOKE ALL ON db_name.tbl_name只授予和撤销表权限。
4.列层级
列权限适用于一个给定表中的单一列,这些权限存储在mysql.columns_priv表中。当使用REVOKE时,必须指定与被授权列相同的列。
5.子程序层级
CREATE ROUTINE、ALTER ROUTINE、EXECUTE和GRANT权限适用于已存储的子程序。这些权限可以被授予为全局层级和数据库层级。而且,除了CREATE ROUTINE外,这些权限可以被授予为子程序层级,并存储在mysql.procs_priv表中。
例19.4下面创建一个管理员,以此来讲解GRANT和REVOKE命令的用法。创建一个管理员,可以输入如图19.4所示的命令。(实例位置:光盘\TM\sl\19\19.4****)
以上命令授予用户名为mr、密码为mr的用户使用所有数据库的所有权限,并允许他向其他人授予这些权限。如果不希望用户在系统中存在,可以按如图19.5所示的方式撤销。

图19.4 创建管理员命令

图19.5 撤销用户命令
现在,按如图19.6所示的方式创建一个没有任何权限的常规用户。

图19.6 创建没有任何权限的常规用户
可以为用户mrsoft授予适当的权限,方式如图19.7所示。

图19.7 授予用户适当的权限命令

如果认为mrsoft权限过高,可以按如图19.8所示的方式减少一些权限。
当用户mrsoft不再需要使用数据库时,可以按如图19.9所示的方式撤销所有的权限。

图19.8 减少权限的命令

图19.9 撤销用户的所有权限

19.3 MySQL数据库安全常见问题
19.3.1 权限更改何时生效
MySQL服务器启动的时候以及使用GRANT和REVOKE语句的时候,服务器会自动读取grant表。但是,既然我们知道这些权限保存在什么地方以及它们是如何保存的,就可以手动修改它们。当手动更新它们的时候,MySQL服务器将不会注意到它们已经被修改了。
我们必须向服务器指出已经对权限进行了修改,有3种方法可以实现这个任务。可以在MySQL命令提示符下(必须以管理员的身份登录进入)输入如下命令。
flush privileges;
这是更新权限最常使用的方法。或者,还可以在操作系统中运行:
mysqladmin flush-privileges
或者是
mysqladmin reload
此后,当用户下次再连接的时候,系统将检查全局级别权限;当下一个命令被执行时,将检查数据库级别的权限;而表级别和列级别权限将在用户下次请求的时候被检查。
19.3.2 设置账户密码
(1)可以用mysqladmin命令在DOS命令窗口中指定密码。
mysqladmin -u user_name -h host_name password "newpwd"
mysqladmin命令重设服务器为host_name,且用户名为user_name的用户的密码,新密码为“newpwd”。
(2)通过set password命令设置用户的密码。
set password for 'jeffrey'@'%' = password('biscuit');
只有以root用户(可以更新mysql数据库的用户)身份登录,才可以更改其他用户的密码。如果没有以匿名用户连接,省略for子句便可以更改自己的密码。
set password = password('biscuit');
(3)在全局级别下使用GRANT USAGE语句(在.)指定某个账户的密码,而不影响账户当前的权限。
GRANT USAGE ON . TO 'jeffrey'@'%' IDENTIFIED BY 'biscuit';
(4)在创建新账户时建立密码,要为password列提供一个具体值。
mysql -u root mysql
INSERT INTO user (Host,User,Password)
-> VALUES('%','jeffrey',PASSWORD('biscuit'));
mysql> FLUSH PRIVILEGES;
(5)更改已有账户的密码,要应用UPDATE语句来设置password列值。
mysql -u root mysql
UPDATE user SET Password = PASSWORD('bagel')
-> WHERE Host = '%' AND User = 'francis';
FLUSH PRIVILEGES;

19.3.3 使密码更安全
(1)在管理级别,切记不能将mysql.user表的访问权限授予任何非管理账户。
(2)采用下面的命令模式来连接服务器,以此来隐藏密码。命令如下。
mysql -u francis -p db_name
Enter password: ********
“*”字符指示输入密码的地方,输入的密码是不可见的。因为它对其他用户不可见,与在命令行上指定它相比,这样进入密码更安全。
(3)如果想要从非交互式方式下运行一个脚本调用一个客户端,就没有从终端输入密码的机会。其最安全的方法是让客户端程序提示输入密码或在适当保护的选项文件中指定密码。
19.4 状态文件和日志文件
MySQL数据目录里还包含许多状态文件和日志文件,如表19.2所示。这些文件默认存放位置是相应的MySQL服务器的数据目录,其默认文件名是在服务器主机名上增加一些后缀而得到的。
表19.2 MySQL的状态文件和日志文件
19.4.1 进程ID文件
MySQL服务器会在启动时把自己的进程ID写入PID文件,等运行结束时又会删除该文件。PID文件是允许服务器本身被其他进程找到的工具。例如,如果运行mysql.server,在系统关闭时,关闭MySQL服务器的脚本检查PID文件以决定它需要向哪个进程发出一个终止信号。
19.4.2 日志文件管理
默认情况下,所有日志创建于mysqld数据目录中。通过刷新日志,可以强制mysqld来关闭和重新打开日志文件(或者在某些情况下切换到一个新的日志)。当执行一个FLUSH LOGS语句或执行mysqladmin flush-logs或mysqladmin refresh时,出现日志刷新。如果正使用MySQL复制功能,从复制服务器将维护更多日志文件,被称为接替日志。日志文件的类型如表19.3所示。
表19.3 日志文件的类型
1.错误日志
错误日志记载着MySQL数据库系统的诊断和出错信息。如果mysqld莫名其妙地“死掉”并且mysqld_safe需要重新启动它,mysqld_safe会在错误日志中写入一条restarted mysqld消息。如果mysqld注意到需要自动检查或者修复一个表,则错误日志中会写入一条消息。
在一些操作系统中,如果mysqld“死掉”,错误日志将包含堆栈跟踪信息。跟踪信息可以用来确定mysqld“死掉”的地方。可以用--log-error[=file_name]选项来指定mysqld保存错误日志文件的位置。如果没有指定file_name值,mysqld使用错误日志名host_name.err,并在数据目录中写入日志文件。如果执行FLUSH LOGS,错误日志用-old重新命名后缀,并且mysqld创建一个新的空日志文件(如果未给出--log-error选项,则不会重新命名)。
如果不指定--log-error,或者(在Windows中)使用--console选项,错误被写入标准错误输出stderr。通常标准输出为服务器的终端。
在Windows中,如果未给出--console选项,错误输出总是写入.err文件。
2.常规查询日志
如果想要知道mysqld内部发生了什么,应该用--log[=file_name]或-l [file_name]选项启动它。如果没有指定file_name的值,默认名是host_name.log。所有连接和语句被记录到日志文件。如果怀疑在客户端发生了错误并想确切地知道该客户端发送给mysqld的语句时,该日志可能非常有用。
mysqld按照它接收的顺序记录语句到查询日志,这可能与执行的顺序不同。与更新日志和二进制日志不同,它们在查询执行后,任何一个锁释放前记录日志(查询日志还包含所有语句,而二进制日志不包含只查询数据的语句)。
服务器重新启动和日志刷新不会产生一般的新查询日志文件(尽管刷新关闭并重新打开一般查询日志文件)。在UNIX操作系统中,可以通过下面的命令重新命名文件并创建一个新文件。
shell> mv hostname.log hostname-old.log
shell> mysqladmin flush-logs
shell> cp hostname-old.log to-backup-directory
shell> rm hostname-old.log
在Windows中,服务器打开日志文件期间不能重新命名日志文件。首先,必须停止服务器;然后重新命名日志文件;最后,重启服务器来创建新的日志文件。
3.二进制日志
二进制日志包含所有更新的数据或者已经潜在更新的数据(例如,没有匹配任何行的一个DELETE)的所有语句。语句以“事件”的形式保存,它描述数据更改。
二进制日志还包含关于每个更新数据库的语句的执行时间信息,但不包含没有修改任何数据的语句。如果想要记录所有语句(例如,为了识别有问题的查询),应使用一般查询日志。
二进制日志的主要目的是在恢复时能够最大可能地更新数据库,因为二进制日志包含备份后进行的所有更新。
二进制日志还用于在主服务器上记录所有将发送给从服务器的语句。
当用--log-bin[=file_name]选项启动时,mysqld写入包含所有更新数据的SQL命令的日志文件。如果未给出file_name值,默认名为-bin后面所跟的主机名。如果给出了文件名,但没有包含路径,则文件被写入数据目录。如果在日志名中提供了扩展名(例如,--log-bin=file_name.extension),则扩展名会被忽略。
mysqld在每个二进制日志名后面添加一个数字扩展名。每次启动服务器或刷新日志时该数字则增加。如果当前的日志大小达到max_binlog_size,还会自动创建新的二进制日志。如果正在使用大的事务,二进制日志超过max_binlog_size,事务全写入一个二进制日志中,绝对不要写入不同的二进制日志中。为了能够使当前用户知道还使用哪个不同的二进制日志文件,mysqld还创建一个二进制日志索引文件,包含所有使用的二进制日志文件的文件名。默认情况下与二进制日志文件的文件名相同,扩展名为.index。可以用--log-bin-index[=file_name]选项更改二进制日志索引文件的文件名。当mysqld在运行时,不应手动编辑该文件;如果这样做将会使mysqld变得混乱。
可以用RESET MASTER语句删除所有二进制日志文件,或用PURGE MASTER LOGS只删除部分二进制文件。
如果系统正进行二进制文件复制,应确保没有从服务器在使用旧的二进制日志文件,方可删除它们。一种方法是每天执行一次mysqladmin flush-logs并删除三天前的所有日志。可以手动删除,或最好使用PURGE MASTER LOGS,该语句还会安全地更新二进制日志索引文件(可以采用日期参数)。
具有SUPER权限的客户端可以通过SET SQL_LOG_BIN=0语句禁止将自己的语句记入二进制记录。可以用mysqlbinlog实用工具检查二进制日志文件。
如果想要重新处理日志的语句,这很有用。例如,可以从二进制日志更新MySQL服务器,方法如下。
shell> mysqlbinlog log-file | mysql -h server_name
如果用户正使用事务,必须使用MySQL二进制日志进行备份,而不能使用旧的更新日志。
查询结束后、锁定被释放前或提交完成后的事务,则立即将数据记入二进制日志,这样可以确保按执行顺序记入日志。
对非事务表的更新执行完毕后立即保存到二进制日志中。对于事务表,如BDB或InnoDB表,所有更改表的更新(UPDATE、DELETE或INSERT)被存入缓存中,直到服务器接收到COMMIT语句。在该点,当用户执行完COMMIT之前,mysqld将整个事务写入二进制日志。当处理事务的线程启动时,它为缓冲查询分配binlog_cache_size大小的内存。如果语句大于该值,线程则打开临时文件来保存事务。线程结束后临时文件被删除。
binlog_cache_use状态变量显示使用该缓冲区(也可能是临时文件)保存语句的事务数量。binlog_cache_disk_use状态变量显示这些事务中实际上有多少必须使用临时文件。这两个变量可以用于将binlog_cache_size调节到足够大的值,以避免使用临时文件。
max_binlog_cache_size(默认4GB)可以用来限制用来缓存多语句事务的缓冲区总大小。如果某个事务大于该值,将会失败并执行回滚操作。
如果正使用更新日志或二进制日志,当使用CREATE ... SELECT or INSERT ... SELECT时,并行插入被转换为普通插入。这样通过在备份时使用日志可以确保重新创建表的备份。
默认情况下,并不是每次写入时都将二进制日志与硬盘同步。因此如果操作系统或机器(不仅是MySQL服务器)崩溃,有可能二进制日志中最后的语句丢失了。要想防止这种情况,可以使用sync_binlog全局变量(1是最安全的值,但也是最慢的),使二进制日志在每N次二进制日志写入后与硬盘同步。即使sync_binlog设置为1,出现崩溃时,也有可能表内容和二进制日志内容之间存在不一致性。例如,如果使用InnoDB表,MySQL服务器处理COMMIT语句,它将整个事务写入二进制日志并将事务提交到InnoDB中。如果在两次操作之间出现崩溃,重启时,事务被InnoDB回滚,但仍然存在二进制日志中。可以用--innodb-safe-binlog选项解决该问题,可以增加InnoDB表内容和二进制日志之间的一致性。

该选项可以提供更大程度的安全,还应对MySQL服务器进行配置,使每个事务的二进制日志(sync_binlog =1)和(默认情况为真)InnoDB日志与硬盘同步。该选项的效果是崩溃后重启时,在滚回事务后,MySQL服务器从二进制日志剪切回滚的InnoDB事务。这样可以确保二进制日志反馈InnoDB表的确切数据等,并使从服务器与主服务器保持同步(不接收回滚的语句)。
注意,即使MySQL服务器更新其他存储引擎而不是InnoDB,也可以使用--innodb-safe-binlog。在InnoDB崩溃恢复时,只从二进制日志中删除影响InnoDB表的语句/事务。如果崩溃恢复时MySQL服务器发现二进制日志变短了(即至少缺少一个成功提交的InnoDB事务),如果sync_binlog =1并且硬盘/文件系统的确能根据需要进行同步(有些不需要)则不会发生,则输出错误消息("二进制日志<名>比期望的要小")。在这种情况下,二进制日志不准确,复制应从主服务器的数据快照开始。
4.慢查询日志
慢查询日志记载着执行用时较长的查询命令,这里所说的“长”是由MySQL服务器变量long_query_time(以秒为单位)定义的。每出现一个慢查询,MySQL服务器就会给它的slow_queries状态计算器加上一个1。
用--log-slow-queries[=file_name]选项启动时,mysqld写一个包含所有执行时间超过long_query_time秒的SQL语句的日志文件。
如果没有给出file_name值,默认为主机名,后缀为-slow.log。如果给出了文件名,但不是绝对路径名,文件则写入数据目录。
语句执行完并且所有锁释放后记入慢查询日志。记录顺序可以与执行顺序不相同。
慢查询日志可以用来找到执行时间长的查询,可以用于优化。但是,检查又长又慢的查询日志会很困难。要想容易些,可以使用mysqldumpslow命令获得日志中显示的查询摘要来处理慢查询日志。
在MySQL 5.1的慢查询日志中,不使用索引的慢查询同使用索引的查询一样记录。要想防止不使用索引的慢查询记入慢查询日志,使用--log-short-format选项。
在MySQL 5.1中,通过--log-slow-admin-statements服务器选项,可以请求将慢管理语句,例如OPTIMIZE TABLE、ANALYZE TABLE和ALTER TABLE写入慢查询日志。
用查询缓存处理的查询不加到慢查询日志中,因为表有零行或一行而不能从索引中受益的查询也不写入慢查询日志。
5.日志文件维护
MySQL服务器可以创建各种不同的日志文件,从而可以很容易地看见所进行的操作。但是,必须定期清理这些文件,确保日志不会占用太多的硬盘空间。
当启用日志使用MySQL时,可能想要不时地备份并删除旧的日志文件,并告诉MySQL开始记入新文件。在Linux (Red Hat)的安装上,可为此使用mysql-log-rotate脚本。如果从RPM分发安装MySQL,脚本应该自动被安装了。
在其他系统上,必须自己安装短脚本,可从cron等入手处理日志文件。可以通过mysqladmin flush-logs或SQL语句FLUSH LOGS来强制MySQL开始使用新的日志文件。
日志清空执行的操作如下。
(1)如果使用标准日志(--log)或慢查询日志(--log-slow-queries),关闭并重新打开日志文件。(默认为mysql.log和hostname
-slow.log)。
(2)如果使用更新日志(--log-update)或二进制日志(--log-bin),关闭日志并且打开有更高序列号的新日志文件。
如果只使用更新日志,只需要重新命名日志文件,然后在备份前清空日志。例如:
shell> cd mysql-data-directory
shell> mv mysql.log mysql.old
shell> mysqladmin flush-logs
然后做备份并删除mysql.old。
6.日志失效处理
激活日志功能的弊病之一是随着日志的增加而产生的大量信息,生成的日志文件有可能会填满整个磁盘。如果MySQL服务器非常繁忙且需要处理大量的查询。用户既想保持有足够的空间来记录MySQL服务器的工作情况日志,又想防止日志文件无限制地增长,就需要应用一些日志文件的失效处理技术。进行日志失效处理的方式主要有以下几种。
1)日志轮转
该方法适用于常规查询日志和慢查询日志这些文件名固定的日志文件,在日志轮转时,应进行日志刷新操作(mysqladmin flush-logs命令或flush logs语句),以确保缓存在内存中的日志信息写入磁盘。
日志轮转的操作过程是这样的(假设日志文件的名字是log):首先,第一次轮转时,把log更名为log.1,然后服务器再创建一个新的log文件;在第二次轮转时,再把log.1更名为log.2,把log更名为log.1,然后服务器再创建一个新的log文件;如此循环,创建一系列的日志文件。当到达日志轮转失效位置时,下次轮转就不再对它进行更名,直接把最后一个日志文件覆盖掉。例如,如果每天进行一次日志轮转并想保留最后7天的日志文件,就需要保留log.1~log.7共7个日志文件,等下次轮转时,用log.6覆盖原来的log.7成新的log.7,原来的log.7就自然失效。
日志轮转的频率和需要保留的老日志时间取决于MySQL服务器的繁忙程度(服务器越繁忙,生成的日志信息就越多)和用户分配用于存放老日志的磁盘空间。
UNIX系统允许对MySQL服务器已经打开并正在使用的当前日志文件进行更名,日志刷新操作将关闭当前日志文件并打开一个新日志文件,用原来的名字创建一个新的日志文件。文件名固定不变的日志文件可以用下面这个shell脚本来进行轮转。
#!/bin/sh
# rotate_fixed_logs.sh - rotate MySQL log file that has a fixed name
# Argument 1:log file name
if [ $# -ne 1 ]; then
echo "Usage: $0 logname" 1>&2
exit 1
if
logfile=$1
mv $logfile.6 $logfile.7
mv $logfile.5 $logfile.6
mv $logfile.4 $logfile.5
mv $logfile.3 $logfile.4
mv $logfile.2 $logfile.3
mv $logfile.1 $logfile.2
mv $logfile $logfile.1
mysqladmin flush-logs
这个脚本以日志文件名作为参数,既可以直接给出日志文件的完整路径名,也可以先进入日志文件所在的目录再给出日志文件的文件名。比如说,如果想对/usr/mysql/data目录名为log的日志进行轮转,可以使用下面这条命令。
% rotate_fixed_logs.sh /usr/mysql/data/log
也可以使用下面的命令。
% cd/usr/mysql/data
% rotate_fixed_logs.sh log
为确保管理员自己总是存在权限对日志文件进行更名,最好是在以mysqladm为登录名上机时运行这个脚本,这里需要注意的是,在这个脚本里的mysqladmin命令行上没有给出-u或-p之类的连接选项参数。
如果用户已经把执行mysql客户程序时要用到的连接参数保存到了mysqladmin程序的my.cnf选项文件里,就不用在这个脚本中的mysqladmin命令行上再次给出它们。
如果用户没有使用选项文件,就必须使用-u和-p选项告诉mysqladmin使用哪个MySQL账户(这个MySQL账户必须具备日志刷新操作所需要的权限)去连接MySQL服务器。这样,MySQL账户的口令将会出现在rotate_fixed_logs.sh脚本的代码里,所以为了防止这个脚本成为一个安全漏洞,这里建议读者专门创建一个除了能对日志进行刷新以外没有其他任何权限的MySQL账户(即一个具备且仅具备RELOAD权限的MySQL账户),将该账户的口令写到脚本代码里,最后再将这个脚本设置成只允许mysqladm用户去编辑和使用。下面这条GRANT语句将以mrsoft为用户名、以mrsoftpass为口令创建出一个如上所述的MySQL账户来。
GRANT RELOAD ON . TO 'flush'@'localhost' IDENTIFIED BY 'mrsoftpass';
创建出这个账户之后,再把rotate_fixed_logs.sh脚本中的mysqladmin命令行改写为如下所示的命令。
mysqladmin –u mrsoft –pmrsoftpass mrsoft-logs
在Linux系统上的MySQL发行版本中带有一个用来安装mysql-log-rotate日志轮转脚本的logrotate工具,所以不必非得使用rotate_fixed_logs.sh或者自行编写其他的类似脚本。如用RPM安装,则在/usr/share/mysql目录;如用二进制方式安装,则在MySQL安装目录的support-files目录;如用源码安装,则在安装目录的share/mysql目录中。
Windows系统上的日志轮转与UNIX系统的不太一样。如果试图对一个已经被MySQL服务器打开并使用着的日志文件进行更名操作,就会发生“file in use”(文件已被打开)错误。要在Windows系统上对日志进行轮转,就得先停止MySQL服务器,然后对文件进行更名,最后再重新启动MySQL服务器,在Windows系统上启动和停止MySQL服务器的步骤前面已经介绍了。下面是一个进行日志更名的批处理文件。
@echo off
REM rotate_fixed_logs.bat – rotate MySQL log file that has a fixed name
if not "%1" == "" goto ROTATE
@echo Usage: rotate_fixed_logs logname
goto DONE
:ROTATE
set logfile=%1
erase %logfile%.7
rename %logfile%.6 %logfile%.7
rename %logfile%.5 %logfile%.6
rename %logfile%.4 %logfile%.5
rename %logfile%.3 %logfile%.4
rename %logfile%.2 %logfile%.3
rename %logfile%.1 %logfile%.2
rename %logfile% %logfile%.1
:DONE
这个批处理程序的用法与rotate_fixed_logs.sh脚本差不多,它也需要提供一个将被轮转的日志文件名作为参数,如下所示。
c:>rotate_log c:\mysql\data\log
或者如下所示。
c:>cd\mysql\data
c:> rotate_fixed_logs log

2)以时间为依据对日志进行失效处理
该方法将定期删除超过指定时间的日志文件,适用于变更日志和二进制日志等文件名用数字编号标识的日志文件。
下面是一个用来对以数字编号作为扩展名的日志文件进行失效处理的脚本。
#!/usr/bin/perl -w
# expire_numbered_logs.pl – look through a set of numbered MySQL
# log files and delete those that are more than a week old.
# Usage: expire_numbered_logs.pl logfile ...
use strict;
die "Usage: $0 logfile ...\n" if @ARGV == 0;
my $max_allowed_age = 7; #max allowed age in days
foreach my $file (@ARGV) #check each argument
{
unlink ($file) if -e $file && -M $file >= $max_allowed_age;
}
exit(0);
以上这个脚本是用Perl语言写的。Perl是一种跨平台的脚本语言,用它编写出来的脚本在UNIX和Windows系统上皆可使用。这个脚本也需要提供一个被轮转的日志文件名作为参数,下面是在UNIX系统上的用法。
% expire_numbered_logs.pl /usr/mysql/data/update.[0-9]*
或者是
% cd/usr/mysql/data
% expire_numbered_logs.pl update.[0-9]*

3)镜像机制
将日志文件镜像到所有的从服务器上,就需要使用镜像机制,用户必须知道主服务器有多少个从服务器,哪些正在运行,并需依次连接每一个从服务器,同时发出show slave status语句以确定它正处理主服务器的哪个二进制日志文件(语句输出列表的Master_Log_File项),只有所有的从服务器都不会用到的日志文件才能删除。例如,本地MySQL服务器是主服务器,它有两个从MySQL服务器S1和S2。在主服务器上有5个二进制日志文件,它们的名字是mrlog0.38~mrlog0.42。
SHOW SLAVE STATUS语句在S1上的执行结果如下。
mysql> SHOW SLAVE STATUS\G
…
Master_Log_File:mrlog.41
…
在S2上的执行结果如下。
mysql> SHOW SLAVE STATUS\G
…
Master_Log_File:mrlog.40
…
这样,我们就知道从服务器仍在使用的、最低编号的二进制日志是mrlog.40,而编号比它更小的那些二进制日志,因为不再有从服务器需要用到它们,所以已经可以安全地删掉。于是,连接到主服务器并发出下面的语句:
mysql> PURGE MASTER LOGS TO 'mrlog.040';
在主服务器上发出的这条命令将把编号小于40的二进制日志文件删除。
19.5 小结
本章对MySQL数据库的账户管理和权限管理的内容进行了详细讲解,其中,账户管理和权限管理是本章的重点内容。这两部分中的密码管理、授权和收回权限是重中之重,因为这些内容涉及MySQL数据库的安全。希望读者能够认真学习这部分的内容。
19.6 实践与练习
1.实现创建一个名称为mr的用户,然后再将其删除。(答案位置:光盘\TM\sl\19\19.5****)
2.使用set password命令将刚刚创建的mr用户的密码设置为111。(答案位置:光盘\TM\sl\19\19.6****)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)