视图在数据库中的作用类似于窗户,用户可以通过这个窗口看到只对自己有用的数据。既保障了数据的安全性,又大大提高了查询效率。
MySQL 视图(View)是一种虚拟存在的表,同真实表一样,视图也由列和行构成,但视图并不实际存在于数据库中。行和列的数据来自于定义视图的查询中所使用的表,并且还是在使用视图时动态生成的。
数据库中只存放了视图的定义,并没有存放视图中的数据,这些数据都存放在定义视图查询所引用的真实表中。使用视图查询数据时,数据库会从真实表中取出对应的数据。因此,视图中的数据是依赖于真实表中的数据的。一旦真实表中的数据发生改变,显示在视图中的数据也会发生改变。
视图可以从原有的表上选取对用户有用的信息,那些对用户没用,或者用户没有权限了解的信息,都可以直接屏蔽掉,作用类似于筛选。这样做既使应用简单化,也保证了系统的安全。
例如,下面的数据库中有一张公司部门表 department。表中包括部门号(d_id)、部门名称(d_name)、功能(function)和办公地址(address)。department 表的结构如下:
mysql> DESC department;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| d_id | int(4) | NO | PRI | NULL | |
| d_name | varchar(20) | NO | UNI | NULL | |
| function | varchar(50) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
4 rows in set (0.02 sec)
还有一张员工表 worker。表中包含了员工的工作号(num)、部门号(d_id)、姓名(name)、性别(sex)、出生日期(birthday)和家庭住址(homeaddress)。worker 表的结构如下:
mysql> DESC worker;
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| num | int(10) | NO | PRI | NULL | |
| d_id | int(4) | YES |MUL | NULL | |
| name | varchar(20) | NO | | NULL | |
| sex | varchar(4) | NO | | NULL | |
| birthday | datetime | YES | | NULL | |
| homeaddress | varchar(50) | YES | | NULL | |
+-------------+-------------+------+-----+---------+-------+
6 rows in set (0.01 sec)
由于各部门领导的权力范围不同,因此,各部门的领导只能看到该部门的员工信息;而且,领导可能不关心员工的生日和家庭住址。为了达到这个目的,可以为各部门的领导建立一个视图,通过该视图,领导只能看到本部门员工的指定信息。
技巧:如果经常需要从多个表查询指定字段的数据,可以在这些表上建立一个视图,通过这个视图显示这些字段的数据。
MySQL 的视图不支持输入参数的功能,因此交互性上还有欠缺。但对于变化不是很大的操作,使用视图可以很大程度上简化用户的操作。
视图并不同于数据表,它们的区别在于以下几点:
视图与表在本质上虽然不相同,但视图经过定义以后,结构形式和表一样,可以进行查询、修改、更新和删除等操作。同时,视图具有如下优点:
在实际的应用过程中,不同的用户可能对不同的数据有不同的要求。
例如,当数据库同时存在时,如学生基本信息表、课程表和教师信息表等多种表同时存在时,可以根据需求让不同的用户使用各自的数据。学生查看修改自己基本信息的视图,安排课程人员查看修改课程表和教师信息的视图,教师查看学生信息和课程信息表的视图。
在使用查询时,很多时候要使用聚合函数,同时还要显示其他字段的信息,可能还需要关联到其他表,语句可能会很长,如果这个动作频繁发生的话,可以创建视图来简化操作。
视图是虚拟的,物理上是不存在的。可以只授予用户视图的权限,而不具体指定使用表的权限,来保护基础数据的安全。
通过使用视图,每个用户不必都定义和存储自己所需的数据,可以共享数据库中的数据,同样的数据只需要存储一次。
通过使用视图,可以重新格式化检索出的数据,并组织输出到其他应用程序中。
视图提供的是对查询操作的封装,本身不包含数据,所呈现的数据是根据视图定义从基础表中检索出来的,如果基础表的数据新增或删除,视图呈现的也是更新后的数据。视图定义后,编写完所需的查询,可以方便地重用该视图。
要注意区别视图和数据表的本质,即视图是基于真实表的一张虚拟的表,其数据来源均建立在真实表的基础上。
使用视图的时候,还应该注意以下几点:
提示:ORDER BY 子句可以用在视图中,但若该视图检索数据的 SELECT 语句中也含有 ORDER BY 子句,则该视图中的 ORDER BY 子句将被覆盖。
创建视图是指在已经存在的 MySQL 数据库表上建立视图。视图可以建立在一张表中,也可以建立在多张表中。
可以使用 CREATE VIEW 语句来创建视图。
语法格式如下:
CREATE VIEW <视图名> AS
语法说明如下。
<视图名>
:指定视图的名称。该名称在数据库中必须是唯一的,不能与其他表或视图同名。
:指定创建视图的 SELECT 语句,可用于查询多个基础表或源视图。对于创建视图中的 SELECT 语句的指定存在以下限制:
视图定义中引用的表或视图必须存在。但是,创建完视图后,可以删除定义引用的表或视图。可使用 CHECK TABLE 语句检查视图定义是否存在这类问题。
视图定义中允许使用 ORDER BY 语句,但是若从特定视图进行选择,而该视图使用了自己的 ORDER BY 语句,则视图定义中的 ORDER BY 将被忽略。
视图定义中不能引用 TEMPORARY 表(临时表),不能创建 TEMPORARY 视图。
WITH CHECK OPTION 的意思是,修改视图时,检查插入的数据是否符合 WHERE 设置的条件。
视图一经定义之后,就可以如同查询数据表一样,使用 SELECT 语句查询视图中的数据,语法和查询基础表的数据一样。
视图用于查询主要应用在以下几个方面:
DESCRIBE 可以用来查看视图,语法如下:
DESCRIBE 视图名;
注意:DESCRIBE 一般情况下可以简写成 DESC,输入这个命令的执行结果和输入 DESCRIBE 是一样的。
创建好视图后,可以通过查看视图的语句来查看视图的字段信息以及详细信息。
查看视图的字段信息与查看数据表的字段信息一样,都是使用 DESCRIBE 关键字来查看的。具体语法如下:
DESCRIBE 视图名;
或简写成:
DESC 视图名;
查看视图的字段内容与查看表的字段内容显示的格式是相同的。因此,更能说明视图实际上也是一张数据表了,不同的是,视图中的数据都来自于数据库中已经存在的表。
在 MySQL 中,SHOW CREATE VIEW 语句可以查看视图的详细定义。其语法如下所示:
SHOW CREATE VIEW 视图名;
所有视图的定义都是存储在 information_schema 数据库下的 views 表中,也可以在这个表中查看所有视图的详细信息,SQL 语句如下:
SELECT * FROM information_schema.views;
不过,通常情况下都是使用 SHOW CREATE VIEW 语句。
修改视图是指修改 MySQL 数据库中存在的视图,当基本表的某些字段发生变化时,可以通过修改视图来保持与基本表的一致性。
可以使用 ALTER VIEW 语句来对已有的视图进行修改。
语法格式如下:
ALTER VIEW <视图名> AS
语法说明如下:
<视图名>
:指定视图的名称。该名称在数据库中必须是唯一的,不能与其他表或视图同名。
:指定创建视图的 SELECT 语句,可用于查询多个基础表或源视图。需要注意的是,对于 ALTER VIEW 语句的使用,需要用户具有针对视图的 CREATE VIEW 和 DROP 权限,以及由 SELECT 语句选择的每一列上的某些权限。
修改视图的定义,除了可以通过 ALTER VIEW 外,也可以使用 DROP VIEW 语句先删除视图,再使用 CREATE VIEW 语句来实现。
视图是一个虚拟表,实际的数据来自于基本表,所以通过插入、修改和删除操作更新视图中的数据,实质上是在更新视图所引用的基本表的数据。
注意:对视图的修改就是对基本表的修改,因此在修改时,要满足基本表的数据定义。
某些视图是可更新的。也就是说,可以使用 UPDATE、DELETE 或 INSERT 等语句更新基本表的内容。对于可更新的视图,视图中的行和基本表的行之间必须具有一对一的关系。
还有一些特定的其他结构,这些结构会使得视图不可更新。更具体地讲,如果视图包含以下结构中的任何一种,它就是不可更新的:
删除视图是指删除 MySQL 数据库中已存在的视图。删除视图时,只能删除视图的定义,不会删除数据。
可以使用 DROP VIEW 语句来删除视图。
语法格式如下:
DROP VIEW <视图名1> [ , <视图名2> …]
其中:<视图名>
指定要删除的视图名。DROP VIEW 语句可以一次删除多个视图,但是必须在每个视图上拥有 DROP 权限。
索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录。
通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列。否则,数据库系统将读取每条记录的所有信息进行匹配。
可以把索引比作新华字典的音序表。例如,要查“库”字,如果不使用音序,就需要从字典的 400 页中逐页来找。但是,如果提取拼音出来,构成音序表,就只需要从 10 多页的音序表中直接查找。这样就可以大大节省时间。
因此,使用索引可以很大程度上提高数据库的查询速度,还有效的提高了数据库系统的性能。
索引就是根据表中的一列或若干列按照一定顺序建立的列值与记录行之间的对应关系表,实质上是一张描述索引列的列值与原表中记录行之间一 一对应关系的有序表。
索引是 MySQL 中十分重要的数据库对象,是数据库性能调优技术的基础,常用于实现数据的快速检索。
在 MySQL 中,通常有以下两种方式访问数据库表的行数据:
顺序访问是在表中实行全表扫描,从头到尾逐行遍历,直到在无序的行数据中找到符合条件的目标数据。
顺序访问实现比较简单,但是当表中有大量数据的时候,效率非常低下。例如,在几千万条数据中查找少量的数据时,使用顺序访问方式将会遍历所有的数据,花费大量的时间,显然会影响数据库的处理性能。
索引访问是通过遍历索引来直接访问表中记录行的方式。
使用这种方式的前提是对表建立一个索引,在列上创建了索引之后,查找数据时可以直接根据该列上的索引找到对应记录行的位置,从而快捷地查找到数据。索引存储了指定列数据值的指针,根据指定的排序顺序对这些指针排序。
简而言之,不使用索引,MySQL 就必须从第一条记录开始读完整个表,直到找出相关的行。表越大,查询数据所花费的时间就越多。如果表中查询的列有一个索引,MySQL 就能快速到达一个位置去搜索数据文件,而不必查看所有数据,这样将会节省很大一部分时间。
索引有其明显的优势,也有其不可避免的缺点。
索引的优点如下:
增加索引也有许多不利的方面,主要如下:
使用索引时,需要综合考虑索引的优点和缺点。
索引可以提高查询速度,但是会影响插入记录的速度。因为,向有索引的表中插入记录时,数据库系统会按照索引进行排序,这样就降低了插入记录的速度,插入大量记录时的速度影响会更加明显。
索引的类型和存储引擎有关,每种存储引擎所支持的索引类型不一定完全相同。MySQL 索引可以从存储方式、逻辑角度和实际使用的角度来进行分类。
根据存储方式的不同,MySQL 中常用的索引在物理上分为 B-树索引和 HASH 索引两类,两种不同类型的索引各有其不同的适用范围。
B-树索引
B-树索引又称为 BTREE 索引,目前大部分的索引都是采用 B-树索引来存储的。
B-树索引是一个典型的数据结构,其包含的组件主要有以下几个:
基于这种树形数据结构,表中的每一行都会在索引上有一个对应值。因此,在表中进行数据查询时,可以根据索引值一步一步定位到数据所在的行。
B-树索引可以进行全键值、键值范围和键值前缀查询,也可以对查询结果进行 ORDER BY 排序。但 B-树索引必须遵循左边前缀原则,要考虑以下几点约束:
哈希索引
哈希(Hash)一般翻译为“散列”,也有直接音译成“哈希”的,就是把任意长度的输入(又叫作预映射,pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。
哈希索引也称为散列索引或 HASH 索引。MySQL 目前仅有 MEMORY 存储引擎和 HEAP 存储引擎支持这类索引。其中,MEMORY 存储引擎可以支持 B-树索引和 HASH 索引,且将 HASH 当成默认索引。
HASH 索引不是基于树形的数据结构查找数据,而是根据索引列对应的哈希值的方法获取表的记录行。哈希索引的最大特点是访问速度快,但也存在下面的一些缺点:
根据索引的具体用途,MySQL 中的索引在逻辑上分为以下 5 类:
普通索引
普通索引是 MySQL 中最基本的索引类型,它没有任何限制,唯一任务就是加快系统对数据的访问速度。
普通索引允许在定义索引的列中插入重复值和空值。
创建普通索引时,通常使用的关键字是 INDEX 或 KEY。
唯一索引
唯一索引与普通索引类似,不同的是创建唯一性索引的目的不是为了提高访问速度,而是为了避免数据出现重复。
唯一索引列的值必须唯一,允许有空值。如果是组合索引,则列值的组合必须唯一。
创建唯一索引通常使用 UNIQUE 关键字。
主键索引
顾名思义,主键索引就是专门为主键字段创建的索引,也属于索引的一种。
主键索引是一种特殊的唯一索引,不允许值重复或者值为空。
创建主键索引通常使用 PRIMARY KEY 关键字。不能使用 CREATE INDEX 语句创建主键索引。
空间索引
空间索引是对空间数据类型的字段建立的索引,使用 SPATIAL 关键字进行扩展。
创建空间索引的列必须将其声明为 NOT NULL,空间索引只能在存储引擎为 MyISAM 的表中创建。
空间索引主要用于地理空间数据类型 GEOMETRY。对于初学者来说,这类索引很少会用到。
全文索引
全文索引主要用来查找文本中的关键字,只能在 CHAR、VARCHAR 或 TEXT 类型的列上创建。在 MySQL 中只有 MyISAM 存储引擎支持全文索引。
全文索引允许在索引列中插入重复值和空值。
不过对于大容量的数据表,生成全文索引非常消耗时间和硬盘空间。
创建全文索引使用 FULLTEXT 关键字。
索引在逻辑上分为以上 5 类,但在实际使用中,索引通常被创建成单列索引和组合索引。
单列索引
单列索引就是索引只包含原表的一个列。在表中的单个字段上创建索引,单列索引只根据该字段进行索引。
单列索引可以是普通索引,也可以是唯一性索引,还可以是全文索引。只要保证该索引只对应一个字段即可。
多列索引
组合索引也称为复合索引或多列索引。相对于单列索引来说,组合索引是将原表的多个列共同组成一个索引。多列索引是在表的多个字段上创建一个索引。该索引指向创建时对应的多个字段,可以通过这几个字段进行查询。但是,只有查询条件中使用了这些字段中第一个字段时,索引才会被使用。
提示:一个表可以有多个单列索引,但这些索引不是组合索引。一个组合索引实质上为表的查询提供了多个索引,以此来加快查询速度。
创建索引是指在某个表的一列或多列上建立一个索引,可以提高对表的访问速度。创建索引对 MySQL数据库的高效运行来说是很重要的。
MySQL 提供了三种创建索引的方法:
使用 CREATE INDEX 语句
可以使用专门用于创建索引的 CREATE INDEX 语句在一个已有的表上创建索引,但该语句不能创建主键。
语法格式:
CREATE <索引名> ON <表名> (<列名> [<长度>] [ ASC | DESC])
语法说明如下:
<索引名>
:指定索引名。一个表可以创建多个索引,但每个索引在该表中的名称是唯一的。<表名>
:指定要创建索引的表名。<列名>
:指定要创建索引的列名。通常可以考虑将查询语句中在 JOIN 子句和 WHERE 子句里经常出现的列作为索引列。<长度>
:可选项。指定使用列前的 length 个字符来创建索引。使用列的一部分创建索引有利于减小索引文件的大小,节省索引列所占的空间。在某些情况下,只能对列的前缀进行索引。索引列的长度有一个最大上限 255 个字节(MyISAM 和 InnoDB 表的最大上限为 1000 个字节),如果索引列的长度超过了这个上限,就只能用列的前缀进行索引。另外,BLOB 或 TEXT 类型的列也必须使用前缀索引。ASC|DESC
:可选项。ASC
指定索引按照升序来排列,DESC
指定索引按照降序来排列,默认为ASC
。使用 CREATE TABLE 语句
索引也可以在创建表(CREATE TABLE)的同时创建。在 CREATE TABLE 语句中添加以下语句。语法格式:
CONSTRAINT PRIMARY KEY [索引类型] (<列名>,…)
在 CREATE TABLE 语句中添加此语句,表示在创建新表的同时创建该表的主键。
语法格式:
KEY | INDEX [<索引名>] [<索引类型>] (<列名>,…)
在 CREATE TABLE 语句中添加此语句,表示在创建新表的同时创建该表的索引。
语法格式:
UNIQUE [ INDEX | KEY] [<索引名>] [<索引类型>] (<列名>,…)
在 CREATE TABLE 语句中添加此语句,表示在创建新表的同时创建该表的唯一性索引。
语法格式:
FOREIGN KEY <索引名> <列名>
在 CREATE TABLE 语句中添加此语句,表示在创建新表的同时创建该表的外键。
在使用 CREATE TABLE 语句定义列选项的时候,可以通过直接在某个列定义后面添加 PRIMARY KEY 的方式创建主键。而当主键是由多个列组成的多列索引时,则不能使用这种方法,只能用在语句的最后加上一个 PRIMARY KRY(<列名>,…) 子句的方式来实现。
使用 ALTER TABLE 语句
CREATE INDEX 语句可以在一个已有的表上创建索引,ALTER TABLE 语句也可以在一个已有的表上创建索引。在使用 ALTER TABLE 语句修改表的同时,可以向已有的表添加索引。具体的做法是在 ALTER TABLE 语句中添加以下语法成分的某一项或几项。
语法格式:
ADD INDEX [<索引名>] [<索引类型>] (<列名>,…)
在 ALTER TABLE 语句中添加此语法成分,表示在修改表的同时为该表添加索引。
语法格式:
ADD PRIMARY KEY [<索引类型>] (<列名>,…)
在 ALTER TABLE 语句中添加此语法成分,表示在修改表的同时为该表添加主键。
语法格式:
ADD UNIQUE [ INDEX | KEY] [<索引名>] [<索引类型>] (<列名>,…)
在 ALTER TABLE 语句中添加此语法成分,表示在修改表的同时为该表添加唯一性索引。
语法格式:
ADD FOREIGN KEY [<索引名>] (<列名>,…)
在 ALTER TABLE 语句中添加此语法成分,表示在修改表的同时为该表添加外键。
创建普通索引时,通常使用 INDEX 关键字。
例子:创建一个表 tb_stu_info,在该表的 height 字段创建普通索引。输入的 SQL 语句和执行过程如下所示。
mysql> CREATE TABLE tb_stu_info
-> (
-> id INT NOT NULL,
-> name CHAR(45) DEFAULT NULL,
-> dept_id INT DEFAULT NULL,
-> age INT DEFAULT NULL,
-> height INT DEFAULT NULL,
-> INDEX(height)
-> );
Query OK,0 rows affected (0.40 sec)
mysql> SHOW CREATE TABLE tb_stu_info\G
*************************** 1. row ***************************
Table: tb_stu_info
Create Table: CREATE TABLE `tb_stu_info` (
`id` int(11) NOT NULL,
`name` char(45) DEFAULT NULL,
`dept_id` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`height` int(11) DEFAULT NULL,
KEY `height` (`height`)
) ENGINE=InnoDB DEFAULT CHARSET=gb2312
1 row in set (0.01 sec)
创建唯一索引,通常使用 UNIQUE 参数。
例子:创建一个表 tb_stu_info2,在该表的 id 字段上使用 UNIQUE 关键字创建唯一索引。输入的 SQL 语句和执行过程如下所示。
mysql> CREATE TABLE tb_stu_info2
-> (
-> id INT NOT NULL,
-> name CHAR(45) DEFAULT NULL,
-> dept_id INT DEFAULT NULL,
-> age INT DEFAULT NULL,
-> height INT DEFAULT NULL,
-> UNIQUE INDEX(height)
-> );
Query OK,0 rows affected (0.40 sec)
mysql> SHOW CREATE TABLE tb_stu_info2\G
*************************** 1. row ***************************
Table: tb_stu_info2
Create Table: CREATE TABLE `tb_stu_info2` (
`id` int(11) NOT NULL,
`name` char(45) DEFAULT NULL,
`dept_id` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`height` int(11) DEFAULT NULL,
UNIQUE KEY `height` (`height`)
) ENGINE=InnoDB DEFAULT CHARSET=gb2312
1 row in set (0.00 sec)
索引创建完成后,可以利用 SQL 语句查看已经存在的索引。在 MySQL 中,可以使用 SHOW INDEX 语句查看表中创建的索引。
查看索引的语法格式如下:
SHOW INDEX FROM <表名> [ FROM <数据库名>]
语法说明如下:
其中各主要参数说明如下:
参数 | 说明 |
---|---|
Table | 表示创建索引的数据表名,这里是 tb_stu_info2 数据表。 |
Non_unique | 表示该索引是否是唯一索引。若不是唯一索引,则该列的值为 1;若是唯一索引,则该列的值为 0。 |
Key_name | 表示索引的名称。 |
Seq_in_index | 表示该列在索引中的位置,如果索引是单列的,则该列的值为 1;如果索引是组合索引,则该列的值为每列在索引定义中的顺序。 |
Column_name | 表示定义索引的列字段。 |
Collation | 表示列以何种顺序存储在索引中。在 MySQL 中,升序显示值“A”(升序),若显示为 NULL,则表示无分类。 |
Cardinality | 索引中唯一值数目的估计值。基数根据被存储为整数的统计数据计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL 使用该索引的机会就越大。 |
Sub_part | 表示列中被编入索引的字符的数量。若列只是部分被编入索引,则该列的值为被编入索引的字符的数目;若整列被编入索引,则该列的值为 NULL。 |
Packed | 指示关键字如何被压缩。若没有被压缩,值为 NULL。 |
Null | 用于显示索引列中是否包含 NULL。若列含有 NULL,该列的值为 YES。若没有,则该列的值为 NO。 |
Index_type | 显示索引使用的类型和方法(BTREE、FULLTEXT、HASH、RTREE)。 |
Comment | 显示评注。 |
删除索引是指将表中已经存在的索引删除掉。不用的索引建议进行删除,因为它们会降低表的更新速度,影响数据库的性能。对于这样的索引,应该将其删除。
在 MySQL 中修改索引可以通过删除原索引,再根据需要创建一个同名的索引,从而实现修改索引的操作。
当不再需要索引时,可以使用 DROP INDEX 语句或 ALTER TABLE 语句来对索引进行删除。
使用 DROP INDEX 语句
语法格式:
DROP INDEX <索引名> ON <表名>
语法说明如下:
<索引名>
:要删除的索引名。<表名>
:指定该索引所在的表名。使用 ALTER TABLE 语句
根据 ALTER TABLE 语句的语法可知,该语句也可以用于删除索引。具体使用方法是将 ALTER TABLE 语句的语法中部分指定为以下子句中的某一项。
注意:如果删除的列是索引的组成部分,那么在删除该列时,也会将该列从索引中删除;如果组成索引的所有列都被删除,那么整个索引将被删除。
索引可以提高查询的速度,但并不是使用带有索引的字段查询时,索引都会起作用。使用索引有几种特殊情况,在这些情况下,有可能使用带有索引的字段查询时,索引并没有起作用,下面重点介绍这几种特殊情况。
索引的设计可以遵循一些已有的原则,创建索引的时候应尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引。
总结:选择索引的最终目的是为了使查询的速度变快,上面给出的原则是最基本的准则,但不能只拘泥于上面的准则。应该在学习和工作中不断的实践,根据应用的实际情况进行分析和判断,选择最合适的索引方式。
在数据库的实际操作中,经常会有需要多条 SQL 语句处理多个表才能完成的操作。
存储过程是一组为了完成特定功能的 SQL 语句集合。使用存储过程的目的是将常用或复杂的工作预先用 SQL 语句写好并用一个指定名称存储起来,这个过程经编译和优化后存储在数据库服务器中,因此称为存储过程。当以后需要数据库提供与已定义好的存储过程的功能相同的服务时,只需调用“CALL存储过程名字”即可自动完成。
常用操作数据库的 SQL 语句在执行的时候需要先编译,然后执行。存储过程则采用另一种方式来执行 SQL 语句。
一个存储过程是一个可编程的函数,它在数据库中创建并保存,一般由 SQL 语句和一些特殊的控制结构组成。当希望在不同的应用程序或平台上执行相同的特定功能时,存储过程尤为合适。
MySQL 5.0 版本以前并不支持存储过程,这使 MySQL 在应用上大打折扣。MySQL 从 5.0 版本开始支持存储过程,既提高了数据库的处理速度,同时也提高了数据库编程的灵活性
存储过程是数据库中的一个重要功能,存储过程可以用来转换数据、数据迁移、制作报表,它类似于编程语言,一次执行成功,就可以随时被调用,完成指定的功能操作。
使用存储过程不仅可以提高数据库的访问效率,同时也可以提高数据库使用的安全性。
对于调用者来说,存储过程封装了 SQL 语句,调用者无需考虑逻辑功能的具体实现过程。只是简单调用即可,它可以被 Java 和 C# 等编程语言调用。
编写存储过程对开发者要求稍微高一些,但这并不影响存储过程的普遍使用,因为存储过程有如下优点:
MySQL 存储过程是一些 SQL 语句的集合,比如有时候我们可能需要一大串的 SQL 语句,或者说在编写 SQL 语句的过程中需要设置一些变量的值,这个时候我们就完全有必要编写一个存储过程。
编写存储过程并不是件简单的事情,但是使用存储过程可以简化操作,且减少冗余的操作步骤,同时,还可以减少操作过程中的失误,提高效率,因此应该尽可能的学会使用存储过程。
下面主要介绍如何创建存储过程。
可以使用 CREATE PROCEDURE 语句创建存储过程,语法格式如下:
CREATE PROCEDURE <过程名> ( [过程参数[,…] ] ) <过程体>
[过程参数[,…] ] 格式
[ IN | OUT | INOUT ] <参数名> <类型>
语法说明如下:
<参数名>
为参数名,<类型>
为参数的类型(可以是任何有效的 MySQL 数据类型)。当有多个参数时,参数列表中彼此间用逗号分隔。存储过程可以没有参数(此时存储过程的名称后仍需加上一对括号),也可以有 1 个或多个参数。
在存储过程的创建中,经常会用到一个十分重要的 MySQL 命令,即 DELIMITER 命令
在 MySQL 中,服务器处理 SQL 语句默认是以分号作为语句结束标志的。然而,在创建存储过程时,存储过程体可能包含有多条 SQL 语句,这些 SQL 语句如果仍以分号作为语句结束符,那么 MySQL 服务器在处理时会以遇到的第一条 SQL 语句结尾处的分号作为整个程序的结束符,而不再去处理存储过程体中后面的 SQL 语句,这样显然不行。
为解决以上问题,通常使用 DELIMITER 命令将结束命令修改为其他字符。语法格式如下:
DELIMITER $$
语法说明如下:
在 MySQL 命令行客户端输入如下 SQL 语句。
mysql > DELIMITER ??
成功执行这条 SQL 语句后,任何命令、语句或程序的结束标志就换为两个问号“??”了。
若希望换回默认的分号“;”作为结束标志,则在 MySQL 命令行客户端输入下列语句即可:
mysql > DELIMITER ;
注意:DELIMITER 和分号“;”之间一定要有一个空格。在创建存储过程时,必须具有 CREATE ROUTINE 权限。
创建好存储过程后,用户可以通过 SHOW ATATUS 语句来查看存储过程的状态,也可以通过 SHOW CREATE 语句来查看存储过程的定义。
MySQL 中可以通过 SHOW STATUS 语句查看存储过程的状态,其基本语法形式如下:
SHOW PROCEDURE STATUS LIKE 存储过程名;
LIKE 存储过程名
用来匹配存储过程的名称,LIKE 不能省略。
MySQL 中可以通过 SHOW CREATE 语句查看存储过程的状态,语法格式如下:
SHOW CREATE PROCEDURE 存储过程名;
SHOW STATUS 语句只能查看存储过程是操作的哪一个数据库、存储过程的名称、类型、谁定义的、创建和修改时间、字符编码等信息。但是,这个语句不能查询存储过程的集体定义,如果需要查看详细定义,需要使用 SHOW CREATE 语句。
存储过程的信息都存储在 information_schema 数据库下的 Routines 表中,可以通过查询该表的记录来查询存储过程的信息,SQL 语句如下:
SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME=存储过程名;
在 information_schema 数据库下的 routines 表中,存储着所有存储过程的定义。所以,使用 SELECT 语句查询 routines 表中的存储过程和函数的定义时,一定要使用 routine_name 字段指定存储过程的名称,否则,将查询出所有的存储过程的定义。
在实际开发过程中,业务需求修改的情况时有发生,所以修改 MySQL 中的存储过程是不可避免的。
MySQL 中通过 ALTER PROCEDURE 语句来修改存储过程。
MySQL 中修改存储过程的语法格式如下:
ALTER PROCEDURE 存储过程名 [ 特征 ... ]
特征
指定了存储过程的特性,可能的取值有:
注意:ALTER PROCEDURE 语句用于修改存储过程的某些特征。如果要修改存储过程的内容,可以先删除原存储过程,再以相同的命名创建新的存储过程;如果要修改存储过程的名称,可以先删除原存储过程,再以不同的命名创建新的存储过程。
存储过程被创建后,就会一直保存在数据库服务器上,直至被删除。当 MySQL 数据库中存在废弃的存储过程时,我们需要将它从数据库中删除。
MySQL 中使用 DROP PROCEDURE 语句来删除数据库中已经存在的存储过程。语法格式如下:
DROP PROCEDURE [ IF EXISTS ] <过程名>
语法说明如下:
注意:存储过程名称后面没有参数列表,也没有括号,在删除之前,必须确认该存储过程没有任何依赖关系,否则会导致其他与之关联的存储过程无法运行。
MySQL 的触发器和存储过程一样,都是嵌入到 MySQL 中的一段程序,是 MySQL 中管理数据的有力工具。不同的是执行存储过程要使用 CALL 语句来调用,而触发器的执行不需要使用 CALL 语句来调用,也不需要手工启动,而是通过对数据表的相关操作来触发、激活从而实现执行。比如当对 student 表进行操作(INSERT,DELETE 或 UPDATE)时就会激活它执行。
触发器与数据表关系密切,主要用于保护表中的数据。特别是当有多个表具有一定的相互联系的时候,触发器能够让不同的表保持数据的一致性。
在 MySQL 中,只有执行 INSERT、UPDATE 和 DELETE 操作时才能激活触发器,其它 SQL 语句则不会激活触发器。
那么为什么要使用触发器呢?比如,在实际开发项目时,我们经常会遇到以下情况:
虽然上述情况实现的业务逻辑不同,但是它们都需要在数据表发生更改时,自动进行一些处理。这时就可以使用触发器处理。
触发器的优点如下:
触发器的缺点如下:
在实际使用中,MySQL 所支持的触发器有三种:INSERT 触发器、UPDATE 触发器和 DELETE 触发器。
在 INSERT 语句执行之前或之后响应的触发器。
使用 INSERT 触发器需要注意以下几点:
在 UPDATE 语句执行之前或之后响应的触发器。
使用 UPDATE 触发器需要注意以下几点:
注意:当触发器设计对触发表自身的更新操作时,只能使用 BEFORE 类型的触发器,AFTER 类型的触发器将不被允许。
在 DELETE 语句执行之前或之后响应的触发器。
使用 DELETE 触发器需要注意以下几点:
总体来说,触发器使用的过程中,MySQL 会按照以下方式来处理错误。
对于事务性表,如果触发程序失败,以及由此导致的整个语句失败,那么该语句所执行的所有更改将回滚;对于非事务性表,则不能执行此类回滚,即使语句失败,失败之前所做的任何更改依然有效。
若 BEFORE 触发程序失败,则 MySQL 将不执行相应行上的操作。
若在 BEFORE 或 AFTER 触发程序的执行过程中出现错误,则将导致调用触发程序的整个语句失败。
仅当 BEFORE 触发程序和行操作均已被成功执行,MySQL 才会执行 AFTER 触发程序。
触发器是与 MySQL 数据表有关的数据库对象,在满足定义条件时触发,并执行触发器中定义的语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性。
在 MySQL 5.7 中,可以使用 CREATE TRIGGER 语句创建触发器。
语法格式如下:
CREATE <触发器名> < BEFORE | AFTER >
ON <表名> FOR EACH Row<触发器主体>
语法说明如下。
注意:每个表都支持 INSERT、UPDATE 和 DELETE 的 BEFORE 与 AFTER,因此每个表最多支持 6 个触发器。每个表的每个事件每次只允许有一个触发器。单一触发器不能与多个事件或多个表关联。
另外,在 MySQL 中,若需要查看数据库中已有的触发器,则可以使用 SHOW TRIGGERS 语句。
在 test_db 数据库中,数据表 tb_emp8 为员工信息表,包含 id、name、deptId 和 salary 字段,数据表 tb_emp8 的表结构如下所示。
mysql> SELECT * FROM tb_emp8;
Empty set (0.07 sec)
mysql> DESC tb_emp8;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(22) | YES | UNI | NULL | |
| deptId | int(11) | NO | MUL | NULL | |
| salary | float | YES | | 0 | |
+--------+-------------+------+-----+---------+-------+
4 rows in set (0.05 sec)
【实例 1】创建一个名为 SumOfSalary 的触发器,触发的条件是向数据表 tb_emp8 中插入数据之前,对新插入的 salary 字段值进行求和计算。输入的 SQL 语句和执行过程如下所示。
mysql> CREATE TRIGGER SumOfSalary
-> BEFORE INSERT ON tb_emp8
-> FOR EACH ROW
-> SET @sum=@sum+NEW.salary;
Query OK, 0 rows affected (0.35 sec)
触发器 SumOfSalary 创建完成之后,向表 tb_emp8 中插入记录时,定义的 sum 值由 0 变成了 1500,即插入值 1000 和 500 的和,如下所示。
SET @sum=0;
Query OK, 0 rows affected (0.05 sec)
mysql> INSERT INTO tb_emp8
-> VALUES(1,'A',1,1000),(2,'B',1,500);
Query OK, 2 rows affected (0.09 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT @sum;
+------+
| @sum |
+------+
| 1500 |
+------+
1 row in set (0.03 sec)
在 test_db 数据库中,数据表 tb_emp6 和 tb_emp7 都为员工信息表,包含 id、name、deptId 和 salary 字段,数据表 tb_emp6 和 tb_emp7 的表结构如下所示。
mysql> SELECT * FROM tb_emp6;
Empty set (0.07 sec)
mysql> SELECT * FROM tb_emp7;
Empty set (0.03 sec)
mysql> DESC tb_emp6;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(25) | YES | | NULL | |
| deptId | int(11) | YES | MUL | NULL | |
| salary | float | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> DESC tb_emp7;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(25) | YES | | NULL | |
| deptId | int(11) | YES | | NULL | |
| salary | float | YES | | 0 | |
+--------+-------------+------+-----+---------+-------+
4 rows in set (0.04 sec)
【实例 2】创建一个名为 double_salary 的触发器,触发的条件是向数据表 tb_emp6 中插入数据之后,再向数据表 tb_emp7 中插入相同的数据,并且 salary 为 tb_emp6 中新插入的 salary 字段值的 2 倍。输入的 SQL 语句和执行过程如下所示。
mysql> CREATE TRIGGER double_salary
-> AFTER INSERT ON tb_emp6
-> FOR EACH ROW
-> INSERT INTO tb_emp7
-> VALUES (NEW.id,NEW.name,deptId,2*NEW.salary);
Query OK, 0 rows affected (0.25 sec)
触发器 double_salary 创建完成之后,向表 tb_emp6 中插入记录时,同时向表 tb_emp7 中插入相同的记录,并且 salary 字段为 tb_emp6 中 salary 字段值的 2 倍,如下所示。
mysql> INSERT INTO tb_emp6
-> VALUES (1,'A',1,1000),(2,'B',1,500);
Query OK, 2 rows affected (0.09 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM tb_emp6;
+----+------+--------+--------+
| id | name | deptId | salary |
+----+------+--------+--------+
| 1 | A | 1 | 1000 |
| 2 | B | 1 | 500 |
+----+------+--------+--------+
3 rows in set (0.04 sec)
mysql> SELECT * FROM tb_emp7;
+----+------+--------+--------+
| id | name | deptId | salary |
+----+------+--------+--------+
| 1 | A | 1 | 2000 |
| 2 | B | 1 | 1000 |
+----+------+--------+--------+
2 rows in set (0.06 sec)
查看触发器是指查看数据库中已经存在的触发器的定义、状态和语法信息等。MySQL 中查看触发器的方法包括 SHOW TRIGGERS 语句和查询 information_schema 数据库下的 triggers 数据表等。本节将详细介绍这两种查看触发器的方法。
在 MySQL 中,可以通过 SHOW TRIGGERS 语句来查看触发器的基本信息,语法格式如下:
SHOW TRIGGERS;
对以上显示信息的说明如下:
SHOW TRIGGERS 语句用来查看当前创建的所有触发器的信息。因为该语句无法查询指定的触发器,所以在触发器较少的情况下,使用该语句会很方便。如果要查看特定触发器的信息或者数据库中触发器较多时,可以直接从 information_schema 数据库中的 triggers 数据表中查找。
在 MySQL 中,所有触发器的信息都存在 information_schema 数据库的 triggers 表中,可以通过查询命令 SELECT 来查看,具体的语法如下:
SELECT * FROM information_schema.triggers WHERE trigger_name= '触发器名';
其中,'触发器名'
用来指定要查看的触发器的名称,需要用单引号引起来。这种方式可以查询指定的触发器,使用起来更加方便、灵活。
示例 2
下面使用 SELECT 命令查看 trigupdate 触发器,SQL 语句如下:
SELECT * FROM information_schema.triggers WHERE TRIGGER_NAME= 'trigupdate'\G
显示信息的说明如下:
上述 SQL 语句也可以不指定触发器名称,这样将查看所有的触发器,SQL 语句如下:
SELECT * FROM information_schema.triggers \G
这个语句会显示 triggers 数据表中所有的触发器信息。
修改触发器可以通过删除原触发器,再以相同的名称创建新的触发器。
与其他 MySQL 数据库对象一样,可以使用 DROP 语句将触发器从数据库中删除。
语法格式如下:
DROP TRIGGER [ IF EXISTS ] [数据库名] <触发器名>
语法说明如下:
注意:删除一个表的同时,也会自动删除该表上的触发器。另外,触发器不能更新或覆盖,为了修改一个触发器,必须先删除它,再重新创建。
使用 DROP TRIGGER 语句可以删除 MySQL 中已经定义的触发器。
【实例】删除 double_salary 触发器,输入的 SQL 语句和执行过程如下所示。
mysql> DROP TRIGGER double_salary;
Query OK, 0 rows affected (0.03 sec)
在数据库管理中,经常要周期性的执行某一命令或 SQL 语句,于是 MySQL 5.1 版本以后就提供了事件,它可以很方便的实现 MySQL 数据库的计划任务,定期运行指定命令,使用起来非常简单方便。
事件(Event)也可称为事件调度器(Event Scheduler),是用来执行定时任务的一组 SQL 集合,可以通俗理解成 MySQL 中的定时器。一个事件可调用一次,也可周期性的启动。
事件可以作为定时任务调度器,取代部分原来只能用操作系统的计划任务才能执行的工作。另外,更值得一提的是,MySQL 的事件可以实现每秒钟执行一个任务,非常适合对实时性要求较高的环境,而操作系统的计划任务只能精确到每分钟一次。
事件和触发器类似,都是在某些事情发生时启动。当数据库启动一条语句的时候,触发器就启动了,而事件是根据调度事件来启动的。由于他们彼此相似,所以事件也称为临时性触发器。
在 MySQL 中,调度器 event_scheduler 负责调用事件。我们可以通过以下几种命令查看事件是否开启,一般情况下默认值为 OFF。SQL 命令和运行结果如下:
mysql> SHOW VARIABLES LIKE 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | OFF |
+-----------------+-------+
1 row in set, 1 warning (0.02 sec)
mysql> SELECT @@event_scheduler;
+-------------------+
| @@event_scheduler |
+-------------------+
| OFF |
+-------------------+
1 row in set (0.00 sec)
mysql> SHOW PROCESSLIST;
+----+------+-----------------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------------+------+---------+------+----------+------------------+
| 2 | root | localhost:56279 | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+----+------+-----------------+------+---------+------+----------+------------------+
1 row in set (0.01 sec)
从结果可以看出,事件没有开启。因为参数 event_scheduler 的值为 OFF,并且在 PROCESSLIST 中查看不到 event_scheduler 的信息。如果参数 event_scheduler 的值为 ON,或者在 PROCESSLIST 中显示了 event_scheduler 的信息,则说明事件已经开启。
开启事件主要通过以下两种方式实现。
可以使用 SET GLOBAL 命令设定全局变量 event_scheduler 的值,开启或关闭事件。将 event_scheduler 参数的值设置为 ON,表示开启事件;设置为 OFF,则关闭事件。
例如,要开启事件可以在命令行窗口中输入以下命令。
mysql> SET GLOBAL event_scheduler = ON ;
Query OK, 0 rows affected (0.06 sec)
mysql> SHOW VARIABLES LIKE 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set, 1 warning (0.01 sec)
结果显示,event_scheduler 的值为 ON,表示事件已经开启。
通过 SET GLOBAL 命令开启或关闭事件,MySQL 重启服务后事件又会回到原来的状态,如果想要始终开启或关闭事件,可以修改 MySQL 配置文件。
在 MySQL 配置文件中找到 [mysqld] 选项,然后在下面添加以下代码开启事件。
event_scheduler = ON
在配置文件中添加代码并保存文件后,重启 MySQL 服务才能生效。
通过该方法开启或关闭事件,重启 MySQL 服务后,不会回到原来的状态。例如,此时重启 MySQL 服务器,然后查看事件是否开启。
mysql> SHOW VARIABLES LIKE 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set, 1 warning (0.01 sec)
结果显示,参数 event_scheduler 的值为 ON,表示已经开启。
在 MySQL 中,可以通过 CREATE EVENT 语句来创建事件,其语法格式如下:
CREATE EVENT [IF NOT EXISTS] event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO event_body;
从上面的语法可以看出,CRATE EVENT 语句由多个子句组成,各子句的详细说明如下表所示。
子句 | 说明 |
---|---|
DEFINER | 可选 用于定义事件执行时检查权限的用户 |
IF NOT EXISTS | 可选 用于判断要创建的事件是否存在 |
EVENT event_name | 必选 用于指定事件名称,event_name 的最大长度为 64 个字符 如果未指定 event_name,则默认为当前的 MySQL 用户名(不区分大小写) |
ON SCHEDULE schedule | 必选 用于定义执行的时间和时间间隔 schedule 表示触发点 |
ON COMPLETION [NOT] PRESERVE | 可选 用于定义事件是否循环执行,即是一次执行还是永久执行,默认为一次执行,即 NOT PRESERVE |
ENABLE | DISABLE | DISABLE ON SLAVE | 可选,用于指定事件的一种属性。 其中,关键字 ENABLE 表示该事件是活动的,即调度器检查事件是否必须调用; 关键字 DISABLE 表示该事件是关闭的,即事件的声明存储到目录中,但是调度器不会检查它是否应该调用; 关键字 DISABLE ON SLAVE 表示事件在从机中是关闭的。 如果不指定以上 3 个选项中的任何一个,默认为 ENABLE |
COMMENT 'comment' | 可选,用于定义事件的注释 |
DO event_body | 必选 用于指定事件启动时所要执行的代码,可以是任何有效的 SQL 语句、存储过程或者一个计划执行的事件。 如果包含多条语句,则可以使用 BEGIN..END 复合结构 |
在 ON SCHEDULE 子句中,参数 schedule 的值为一个 AT 子句,用于指定事件在某个时刻发生,其语法格式如下:
AT timestamp [+ INTERVAL interval]...
| EVERY interval
[STARTS timestamp [+ INTERVAL interval] ...]
[ENDS timestamp[+ INTERVAL interval]...]
参数说明如下:
interval 参数可以是以下值:
**YEAR** | QUARTER | **MONTH** | **DAY** | **HOUR** | **MINUTE** |
WEEK | **SECOND** | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |
DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND
一般情况下,不建议使用不标准(以上未加粗关键字)的时间单位。
例子:在 test 数据库中创建一个名称为 e_test 的事件,用于每隔 5 秒向表 tb_eventtest 中插入一条数据。
创建 tb_eventtest 表,SQL 语句和运行结果如下:
mysql> CREATE TABLE tb_eventtest(
-> id INT(11) PRIMARY KEY AUTO_INCREMENT,
-> user VARCHAR(20),
-> createtime DATETIME);
Query OK, 0 rows affected (0.07 sec)
创建 e_test 事件,SQL 语句和运行结果如下:
mysql> CREATE EVENT IF NOT EXISTS e_test ON SCHEDULE EVERY 5 SECOND
-> ON COMPLETION PRESERVE
-> DO INSERT INTO tb_eventtest(user,createtime)VALUES('MySQL',NOW());
Query OK, 0 rows affected (0.04 sec)
创建事件后,查询 tb_eventtest 中的数据,SQL 语句和运行结果如下:
mysql> SELECT * FROM tb_eventtest;
+----+-------+---------------------+
| id | user | createtime |
+----+-------+---------------------+
| 1 | MySQL | 2020-05-21 10:41:39 |
| 2 | MySQL | 2020-05-21 10:41:44 |
| 3 | MySQL | 2020-05-21 10:41:49 |
| 4 | MySQL | 2020-05-21 10:41:54 |
+----+-------+---------------------+
4 rows in set (0.01 sec)
从结果可以看出,系统每隔 5 秒插入一条数据,这说明事件创建执行成功了。
创建好事件后,用户可以通过以下 3 种方式来查看事件的状态信息:
查看 information_schema.events 表中的事件状态信息。SQL 语句和运行结果如下:
mysql> SELECT * FROM information_schema.events limit 1\G
*************************** 1. row ***************************
EVENT_CATALOG: def
EVENT_SCHEMA: test
EVENT_NAME: e_test
DEFINER: root@localhost
TIME_ZONE: SYSTEM
EVENT_BODY: SQL
EVENT_DEFINITION: INSERT INTO tb_eventtest(user,createtime)VALUES('MySQL',NOW())
EVENT_TYPE: RECURRING
EXECUTE_AT: NULL
INTERVAL_VALUE: 5
INTERVAL_FIELD: SECOND
SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
STARTS: 2020-05-21 10:41:39
ENDS: NULL
STATUS: ENABLED
ON_COMPLETION: PRESERVE
CREATED: 2020-05-21 10:41:39
LAST_ALTERED: 2020-05-21 10:41:39
LAST_EXECUTED: 2020-05-21 12:38:54
EVENT_COMMENT:
ORIGINATOR: 1
CHARACTER_SET_CLIENT: gbk
COLLATION_CONNECTION: gbk_chinese_ci
DATABASE_COLLATION: utf8_unicode_ci
1 row in set (0.08 sec)
以上参数说明如下表所示:
参数名 | 说明 |
---|---|
EVENT_CATALOG | 事件存放目录,一般情况下,值为 def,不建议修改 |
EVENT_SCHEMA | 事件所在的数据库 |
EVENT_NAME | 事件名称 |
DEFINER | 事件的定义者 |
TIME_ZONE | 事件使用的时区,默认是 SYSTEM,不建议修改 |
EVENT_BODY | 一般情况下,值为 SQL,不建议修改 |
EVENT_DEFINITION | 该事件的内容,可以是具体的 INSERT 等 SQL,也可以是一个调用的存储过程 |
EVENT_TYPE | 事件类型,这个参数比较重要,在定义时指定 有两个值:RECURRING 和 ONE TIME RECURRING 表示只要符合条件就会重复执行,RECURRING 类型的事件一般为 NULL,表示该事件的预计执行时间 ONE TIME 只会调用 EXECUTE_AT,针对 one-time 类型的事件有效 |
INTERVAL_VALUE | 针对 RECURRING 类型的事件有效,表示执行间隔长度 |
INTERVAL_FIELD | 针对 RECURRING 类型的事件有效,表示执行间隔的单位,一般是 SECOND,DAY 等值,可参考创建语法 |
SQL_MODE | 当前事件采用的 SQL_MODE |
STARTS | 针对 RECURRING 类型的事件有效,表示一个事件从哪个时间点开始执行,和 one-time 的 EXECUTE_AT 功能类似。 为 NULL 时表示一符合条件就开始执行 |
ENDS | 针对 RECURRING 类型的事件有效,表示一个事件到了哪个时间点后不再执行,如果为 NULL 就是永不停止 |
STATUS | 一般有三个值,ENABLED、DISABLED 和 SLAVESIDE_DISABLED |
ON_COMPLETION | 只有两个值,PRESERVE 和 NOT PRESERVE |
CREATED | 事件的创建时间 |
LAST_ALTERED | 事件最近一次被修改的时间 |
LAST_EXECUTED | 事件最近一次执行的时间,如果为 NULL 表示从未执行过 |
EVENT_COMMENT | 事件的注释信息 |
ORIGINATOR | 当前事件创建时的 server-id,用于主从上的处理,比如 SLAVESIDE_DISABLED |
CHARACTER_SET_CLIENT | 事件创建时的客户端字符集 |
COLLATION_CONNECTION | 事件创建时的连接字符校验规则 |
DATABASE_COLLATION | 事件创建时的数据库字符集校验规则 |
在 MySQL 中,事件创建之后,可以使用 ALTER EVENT 语句修改其定义和相关属性。
修改事件的语法格式如下:
ALTER EVENT event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO event_body;
ALTER EVENT 语句还有一个用法就是让一个事件关闭或再次让其活动。
例子:修改 e_test 事件,让其每隔 30 秒向表 tb_eventtest 中插入一条数据,SQL 语句和运行结果如下所示:
mysql> ALTER EVENT e_test ON SCHEDULE EVERY 30 SECOND
-> ON COMPLETION PRESERVE
-> DO INSERT INTO tb_eventtest(user,createtime) VALUES('MySQL',NOW());
Query OK, 0 rows affected (0.04 sec)
mysql> TRUNCATE TABLE tb_eventtest;
Query OK, 0 rows affected (0.04 sec)
mysql> SELECT * FROM tb_eventtest;
+----+-------+---------------------+
| id | user | createtime |
+----+-------+---------------------+
| 1 | MySQL | 2020-05-21 13:23:49 |
| 2 | MySQL | 2020-05-21 13:24:19 |
+----+-------+---------------------+
2 rows in set (0.00 sec)
由结果可以看出,修改事件后,表 tb_eventtest 中的数据由原来的每 5 秒插入一条,变为每 30 秒插入一条。
使用 ALTER EVENT 语句还可以临时关闭一个已经创建的事件。
例子:临时关闭事件 e_test 的具体代码如下所示:
mysql> ALTER EVENT e_test DISABLE;
Query OK, 0 rows affected (0.00 sec)
查询 tb_eventtest 表中的数据,SQL 语句如下:
SELECT * FROM tb_eventtest;
为了确定事件已关闭,可以查询两次(每次间隔 1 分钟)tb_eventtest 表的数据,SQL 语句和运行结果如下所示:
mysql> TRUNCATE TABLE tb_eventtest;
Query OK, 0 rows affected (0.05 sec)
mysql> SELECT * FROM tb_eventtest;
Empty set (0.00 sec)
mysql> SELECT * FROM tb_eventtest;
Empty set (0.00 sec)
由结果可以看出,临时关闭事件后,系统就不再继续向表 tb_eventtest 中插入数据了。
在 MySQL 中,可以使用 DROP EVENT 语句删除已经创建的事件。语法格式如下:
DROP EVENT [IF EXISTS] event_name;
例子:删除事件 e_test,SQL 语句和运行结果如下:
mysql> DROP EVENT IF EXISTS e_test;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT * FROM information_schema.events \G
Empty set (0.00 sec)
存储函数和存储过程一样,都是在数据库中定义一些 SQL 语句的集合。存储函数可以通过 return 语句返回函数值,主要用于计算并返回一个值。而存储过程没有直接返回值,主要用于执行操作。
在 MySQL 中,使用 CREATE FUNCTION 语句来创建存储函数,其语法形式如下:
CREATE FUNCTION sp_name ([func_parameter[...]])
RETURNS type
[characteristic ...] routine_body
其中:
注意:在具体创建函数时,函数名不能与已经存在的函数名重名。除了上述要求外,推荐函数名命名(标识符)为 function_xxx 或者 func_xxx。
func_parameter 可以由多个参数组成,其中每个参数由参数名称和参数类型组成,其形式如下:
[IN | OUT | INOUT] param_name type;
其中:
创建函数与创建存储过程一样,需要通过命令 DELIMITER //
将 SQL 语句的结束符由“;”修改为“//”,最后通过命令 DELIMITER ;
将结束符号修改成 SQL 语句中默认的结束符号。
如果在存储函数中的 RETURN 语句返回一个类型不同于函数的 RETURNS 子句中指定类型的值,返回值将被强制为恰当的类型。比如,如果一个函数返回一个 ENUM 或 SET 值,但是 RETURN 语句返回一个整数,对于 SET 成员集的相应的 ENUM 成员,从函数返回的值是字符串。
由于存储函数和存储过程的查看、修改、删除等操作几乎相同,所以我们不再详细讲解如何操作存储函数了。
查看存储函数的语法如下:
SHOW FUNCTION STATUS LIKE 存储函数名;
SHOW CREATE FUNCTION 存储函数名;
SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME=存储函数名;
可以发现,操作存储函数和操作存储过程不同的是将 PROCEDURE 替换成了 FUNCTION。同样,修改存储函数的语法如下:
ALTER FUNCTION 存储函数名 [ 特征 ... ]
存储函数的特征与存储过程的基本一样。
删除存储过程的语法如下:
DROP FUNCTION [ IF EXISTS ] <函数名>
存储过程和存储函数都是存储在服务器端的 SQL 语句集合。要想使用这些已经定义好的存储过程和存储函数就必须要通过调用的方式来实现。
存储过程通过 CALL 语句来调用,存储函数的使用方法与 MySQL 内部函数的使用方法相同。执行存储过程和存储函数需要拥有 EXECUTE 权限(EXECUTE 权限的信息存储在 information_schema 数据库下的 USER_PRIVILEGES 表中)。
MySQL 中使用 CALL 语句来调用存储过程。调用存储过程后,数据库系统将执行存储过程中的 SQL 语句,然后将结果返回给输出值。
CALL 语句接收存储过程的名字以及需要传递给它的任意参数,基本语法形式如下:
CALL sp_name([parameter[...]]);
其中,sp_name 表示存储过程的名称,parameter 表示存储过程的参数。
因为存储过程实际上也是一种函数,所以存储过程名后需要有( )
符号,即使不传递参数也需要。
在 MySQL 中,存储函数的使用方法与 MySQL 内部函数的使用方法是一样的。换言之,用户自己定义的存储函数与 MySQL 内部函数是一个性质的。区别在于,存储函数是用户自己定义的,而内部函数是 MySQL 开发者定义的。
在 MySQL 中,除了支持标准的存储过程和函数外,还引入了表达式。表达式与其它高级语言的表达式一样,由变量、运算符和流程控制来构成。
变量是表达式语句中最基本的元素,可以用来临时存储数据。在存储过程和函数中都可以定义和使用变量。用户可以使用 DECLARE 关键字来定义变量,定义后可以为变量赋值。这些变量的作用范围是 BEGIN...END 程序段中。
MySQL 中可以使用 DECLARE 关键字来定义变量,其基本语法如下:
DECLARE var_name[,...] type [DEFAULT value]
其中:
MySQL 中可以使用 SET 关键字来为变量赋值,SET 语句的基本语法如下:
SET var_name = expr[,var_name = expr]...
其中:
注意:一个 SET 语句可以同时为多个变量赋值,各个变量的赋值语句之间用逗号隔开。
MySQL 中还可以使用 SELECT..INTO 语句为变量赋值。其基本语法如下:
SELECT col_name [...] INTO var_name[,...]
FROM table_name WEHRE condition
其中:
注意:当将查询结果赋值给变量时,该查询语句的返回结果只能是单行。
在程序的运行过程中可能会遇到问题,此时我们可以通过定义条件和处理程序来事先定义这些问题。
定义条件是指事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应当采取的处理方式和解决办法,保证存储过程和函数在遇到警告或错误时能继续执行,从而增强程序处理问题的能力,避免程序出现异常被停止执行。
MySQL 中可以使用 DECLARE 关键字来定义条件。其基本语法如下:
DECLARE condition_name CONDITION FOR condition_value
condition value:
SQLSTATE [VALUE] sqlstate_value | mysql_error_code
其中:
MySQL 中可以使用 DECLARE 关键字来定义处理程序。其基本语法如下:
DECLARE handler_type HANDLER FOR condition_value[...] sp_statement
handler_type:
CONTINUE | EXIT | UNDO
condition_value:
SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code
其中,handler_type 参数指明错误的处理方式,该参数有 3 个取值。这 3 个取值分别是 CONTINUE、EXIT 和 UNDO。
注意:通常情况下,执行过程中遇到错误应该立刻停止执行下面的语句,并且撤回前面的操作。但是,MySQL 中现在还不能支持 UNDO 操作。因此,遇到错误时最好执行 EXIT 操作。如果事先能够预测错误类型,并且进行相应的处理,那么可以执行 CONTINUE 操作。
参数指明错误类型,该参数有 6 个取值:
sp_statement 参数为程序语句段,表示在遇到定义的错误时,需要执行的一些存储过程或函数。
例子:下面是定义处理程序的几种方式,代码如下:
//方法一:捕获 sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info='CAN NOT FIND';
//方法二:捕获 mysql_error_code
DECLARE CONTINUE HANDLER FOR 1146 SET @info='CAN NOT FIND';
//方法三:先定义条件,然后调用
DECLARE can_not_find CONDITION FOR 1146;
DECLARE CONTINUE HANDLER FOR can_not_find SET @info='CAN NOT FIND';
//方法四:使用 SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';
//方法五:使用 NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info='CAN NOT FIND';
//方法六:使用 SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';
上述代码是 6 种定义处理程序的方法。
定义条件和处理顺序,具体的执行过程如下:
mysql> CREATE TABLE t8(s1 INT,PRIMARY KEY(s1));
Query OK, 0 rows affected (0.07 sec)
mysql> DELIMITER //
mysql> CREATE PROCEDURE handlerdemo()
-> BEGIN
-> DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @X2=1;
-> SET @X=1;
-> INSERT INTO t8 VALUES(1);
-> SET @X=2;
-> INSERT INTO t8 VALUES(1);
-> SET @X=3;
-> END //
Query OK, 0 rows affected (0.02 sec)
mysql> DELIMITER ;
mysql> CALL handlerdemo();
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT @X;
+------+
| @X |
+------+
| 3 |
+------+
1 row in set (0.00 sec)
上述代码中,@X 是一个用户变量,执行结果 @X 等于 3,这表明 MySQL 执行到程序的末尾。
如果DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @X2=1;
这一行不存在,第二个 INSERT 因 PRIMARY KEY 约束而失败之后,MySQL 可能已经采取 EXIT 策略,且 SELECT @X 可能已经返回 2。
注意:@X 表示用户变量,使用 SET 语句为其赋值,用户变量与连接有关,一个客户端定义的变量不能被其他客户端所使用,当客户端退出时,该客户端连接的所有变量将自动释放。
在 MySQL 中,存储过程或函数中的查询有时会返回多条记录,而使用简单的 SELECT 语句,没有办法得到第一行、下一行或前十行的数据,这时可以使用游标来逐条读取查询结果集中的记录。游标在部分资料中也被称为光标。
关系数据库管理系统实质是面向集合的,在 MySQL 中并没有一种描述表中单一记录的表达形式,除非使用 WHERE 子句来限制只有一条记录被选中。所以有时我们必须借助于游标来进行单条记录的数据处理。
一般通过游标定位到结果集的某一行进行数据修改。结果集是符合 SQL 语句的所有记录的集合。
个人理解游标就是一个标识,用来标识数据取到了什么地方,可以把他理解成数组中的下标。
不像多数 DBMS,MySQL 游标只能用于存储过程和函数。
MySQL 中使用 DECLARE 关键字来声明游标,并定义相应的 SELECT 语句,根据需要添加 WHERE 和其它子句。其语法的基本形式如下:
DECLARE cursor_name CURSOR FOR select_statement;
其中,cursor_name 表示游标的名称;select_statement 表示 SELECT 语句,可以返回一行或多行数据。
声明游标之后,要想从游标中提取数据,必须首先打开游标。在 MySQL 中,打开游标通过 OPEN 关键字来实现,其语法格式如下:
OPEN cursor_name;
其中,cursor_name 表示所要打开游标的名称。需要注意的是,打开一个游标时,游标并不指向第一条记录,而是指向第一条记录的前边。
在程序中,一个游标可以打开多次。用户打开游标后,其他用户或程序可能正在更新数据表,所以有时会导致用户每次打开游标后,显示的结果都不同。
游标顺利打开后,可以使用 FETCH...INTO 语句来读取数据,其语法形式如下:
FETCH cursor_name INTO var_name [,var_name]...
上述语句中,将游标 cursor_name 中 SELECT 语句的执行结果保存到变量参数 var_name 中。变量参数 var_name 必须在游标使用之前定义。使用游标类似高级语言中的数组遍历,当第一次使用游标时,此时游标指向结果集的第一条记录。
MySQL 的游标是只读的,也就是说,你只能顺序地从开始往后读取结果集,不能从后往前,也不能直接跳到中间的记录。
游标使用完毕后,要及时关闭,在 MySQL 中,使用 CLOSE 关键字关闭游标,其语法格式如下:
CLOSE cursor_name;
CLOSE 释放游标使用的所有内部内存和资源,因此每个游标不再需要时都应该关闭。
在一个游标关闭后,如果没有重新打开,则不能使用它。但是,使用声明过的游标不需要再次声明,用 OPEN 语句打开它就可以了。
如果你不明确关闭游标,MySQL 将会在到达 END 语句时自动关闭它。游标关闭之后,不能使用 FETCH 来使用该游标。
在存储过程和自定义函数中可以使用流程控制语句来控制程序的流程。MySQL 中流程控制语句有:IF 语句、CASE 语句、LOOP 语句、LEAVE 语句、ITERATE 语句、REPEAT 语句和 WHILE 语句等。
IF 语句用来进行条件判断,根据是否满足条件(可包含多个条件),来执行不同的语句,是流程控制中最常用的判断语句。其语法的基本形式如下:
IF search_condition THEN statement_list
[ELSEIF search_condition THEN statement_list]...
[ELSE statement_list]
END IF
其中,search_condition 参数表示条件判断语句,如果返回值为 TRUE ,相应的 SQL 语句列表(statement_list)被执行;如果返回值为 FALSE,则 ELSE 子句的语句列表被执行。statement_list 可以包括一个或多个语句。
注意:MySQL 中的 IF( ) 函数不同于这里的 IF 语句。
CASE 语句也是用来进行条件判断的,它提供了多个条件进行选择,可以实现比 IF 语句更复杂的条件判断。CASE 语句的基本形式如下:
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list]...
[ELSE statement_list]
END CASE
其中:
CASE 语句还有另一种形式。该形式的语法如下:
CASE
WHEN search_condition THEN statement_list
[WHEN search_condition THEN statement_list] ...
[ELSE statement_list]
END CASE
其中,search_condition 参数表示条件判断语句;statement_list 参数表示不同条件的执行语句。
与上述语句不同的是,该语句中的 WHEN 语句将被逐个执行,直到某个 search_condition 表达式为真,则执行对应 THEN 关键字后面的 statement_list 语句。如果没有条件匹配,ELSE 子句里的语句被执行。
这里介绍的 CASE 语句与“控制流程函数”里描述的 SQL CASE 表达式的 CASE 语句有轻微的不同。这里的 CASE 语句不能有 ELSE NULL 语句,并且用 END CASE 替代 END 来终止。
LOOP 语句可以使某些特定的语句重复执行。与 IF 和 CASE 语句相比,LOOP 只实现了一个简单的循环,并不进行条件判断。
LOOP 语句本身没有停止循环的语句,必须使用 LEAVE 语句等才能停止循环,跳出循环过程。LOOP 语句的基本形式如下:
[begin_label:]LOOP
statement_list
END LOOP [end_label]
其中,begin_label 参数和 end_label 参数分别表示循环开始和结束的标志,这两个标志必须相同,而且都可以省略;statement_list 参数表示需要循环执行的语句。
LEAVE 语句主要用于跳出循环控制。其语法形式如下:
LEAVE label
其中,label 参数表示循环的标志,LEAVE 语句必须跟在循环标志前面。
ITERATE 是“再次循环”的意思,用来跳出本次循环,直接进入下一次循环。ITERATE 语句的基本语法形式如下:
ITERATE label
其中,label 参数表示循环的标志,ITERATE 语句必须跟在循环标志前面。
说明:LEAVE 语句和 ITERATE 语句都用来跳出循环语句,但两者的功能是不一样的。LEAVE 语句是跳出整个循环,然后执行循环后面的程序。而 ITERATE 语句是跳出本次循环,然后进入下一次循环。使用这两个语句时一定要区分清楚。
REPEAT 语句是有条件控制的循环语句,每次语句执行完毕后,会对条件表达式进行判断,如果表达式返回值为 TRUE,则循环结束,否则重复执行循环中的语句。
REPEAT 语句的基本语法形式如下:
[begin_label:] REPEAT
statement_list
UNTIL search_condition
END REPEAT [end_label]
其中:
WHILE 语句也是有条件控制的循环语句。WHILE 语句和 REPEAT 语句不同的是,WHILE 语句是当满足条件时,执行循环内的语句,否则退出循环。WHILE 语句的基本语法形式如下:
[begin_label:] WHILE search_condition DO
statement list
END WHILE [end label]
其中,search_condition 参数表示循环执行的条件,满足该条件时循环执行;statement_list 参数表示循环的执行语句。WHILE 循环需要使用 END WHILE 来结束。