mysql之从入门到删库跑路

相对来说写的还算全的,基本上工作当中能用到的也就这些了,好脑子不如烂笔头,存储过程、存储函数、引擎、事务、定时任务、触发器、变量等等都包含在内了,感兴趣的可以收藏的哦!

目录

1、连接mysql

mysql分客户端和服务端,客户端有Navcat、sqlyog等等,如果用客户端的话,这个命令也用不到,但是如果电脑只安装了服务端,而且还想查看数据库,这时候就需要用到了,打开电脑cmd命令窗口,执行就可以了,如果是linux,直接执行就可以了。

客服端他无非就是帮我们将mysql底层的命令,封装成了可视化软件,仅此而已,底层逻辑是不变的。

mysql  -h  主机名(ip)  -u  用户名 -P 端口 -p 密码

示例:

mysql -h127.0.0.1 -uroot -P3306 -proot

2、Mysql语句分类

SQL语句:结构化查询语句,使用SQL与数据库“沟通”,完成相应的数据库操作。

DDL(Data Definition Languages)语句:即数据库定义语句,用来创建数据库中的表、索引、视图、存储过程、触发器等,常用的语句关键字有:CREATE,ALTER,DROP,TRUNCATE,COMMENT,RENAME。增删改表的结构

DML(Data Manipulation Language)语句:即数据操纵语句,用来查询、添加、更新、删除等,常用的语句关键字有:SELECT,INSERT,UPDATE,DELETE,MERGE,CALL,EXPLAIN PLAN,LOCK TABLE,包括通用性的增删改查。增删改表的数据

DCL(Data Control Language)语句:即数据控制语句,用于授权/撤销数据库及其字段的权限(DCL is short name of Data Control Language which includes commands such as GRANT and mostly concerned with rights, permissions and other controls of the database system.)。常用的语句关键字有:GRANT,REVOKE。

TCL(Transaction Control Language)语句:事务控制语句,用于控制事务,常用的语句关键字有:COMMIT,ROLLBACK,SAVEPOINT,SET TRANSACTION。

3、数据库命令操作

3.1、显示数据库

mysql> SHOW DATABASES;

3.2、创建数据库

CREATE DATABASE 数据库名 CHARSET='编码格式'
mysql> CREATE DATABASE testa CHARSET = 'utf8';

3.3、使用数据库

使用当前数据库:

use 数据库名;

3.4、查看当前数据库

SELECT DATABASE();

3.5、删除数据库

DROP DATABASE 数据库名;

4、数据类型

4.1、数值类型

在这里插入图片描述

4.2、日期和时间类型

在这里插入图片描述

4.3、字符串类型

在这里插入图片描述
注意:

  1. char类型的长度是固定的,varchar的长度是可变的。
    这就表示,存储字符串’abc’,使用char(10),表示存储的字符将占10个字节(包括7个空字符)
    使用varchar2(10),,则表示只占3个字节,10是最大值,当存储的字符小于10时,按照实际的长度存储。

  2. varchar2比char节省空间,但是在效率上比char稍差些。既要获得效率即必须牺牲一点空间,这就是设计上的"以空间换时间"

  3. BLOB 是一个二进制大对象,可以容纳可变数量的数据。有 4 种 BLOB 类型:TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB。它们区别在于可容纳存储范围不同。

  4. 有 4 种 TEXT 类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。对应的这 4 种 BLOB 类型,可存储的最大长度不同,可根据实际情况选择。

5、表操作

5.1、创建表

创建表的时候if not exists可有可无:
存在代表的是如果表已经创建,不会报错,但是并不会覆盖创建好的表。不存在if not exists创建表的话就是如果表已经存在,直接运行报错。

如果线上发版,建议带上,因为发版sql一般都很多,假如创建表的语句后面还跟着一些sql语句,但是表已经存在了,这时候就会导致sql直接终止。带上的话表存在也不影响后面sql正常执行。

CREATE TABLE IF NOT EXISTS `表名` (
	`字段名` 列类型 [属性] [注释]
	`字段名` 列类型 [属性] [注释],
	索引类型 (`字段名`)
) [表类型] [字符集设置]

示例:
AUTO_INCREMENT:id自增
NOT NULL:插入的时候不允许为空
PRIMARY KEY:主键

CREATE TABLE IF NOT EXISTS `runoob_tbl`(
   `runoob_id` INT UNSIGNED AUTO_INCREMENT COMMENT '注释',
   `runoob_title` VARCHAR(100) NOT NULL COMMENT '注释',
   `runoob_author` VARCHAR(40) NOT NULL COMMENT '注释',
   `submission_date` DATE,
   PRIMARY KEY ( `runoob_id` ),
	 UNIQUE (`runoob_title`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

5.2、创建临时表

MySQL 临时表在我们需要保存一些临时数据时是非常有用的。临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间。

临时表在MySQL 3.23版本中添加,如果你的MySQL版本低于 3.23版本就无法使用MySQL的临时表。

mysql> CREATE TEMPORARY TABLE SalesSummary (
    -> product_name VARCHAR(50) NOT NULL
    -> , total_sales DECIMAL(12,2) NOT NULL DEFAULT 0.00
    -> , avg_unit_price DECIMAL(7,2) NOT NULL DEFAULT 0.00
    -> , total_units_sold INT UNSIGNED NOT NULL DEFAULT 0
);
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO SalesSummary
    -> (product_name, total_sales, avg_unit_price, total_units_sold)
    -> VALUES
    -> ('cucumber', 100.25, 90, 2);

mysql> SELECT * FROM SalesSummary;
+--------------+-------------+----------------+------------------+
| product_name | total_sales | avg_unit_price | total_units_sold |
+--------------+-------------+----------------+------------------+
| cucumber     |      100.25 |          90.00 |                2 |
+--------------+-------------+----------------+------------------+
1 row in set (0.00 sec)

删除临时表

DROP TABLE SalesSummary;

5.3、更新表

5.3.1、添加列

ALTER TABLE 表名 ADD 字段名 数据类型(长度) COMMENT "注释";

示例:在runoob_tbl创建一个字段为bb不为空的属性。

ALTER TABLE runoob_tbl ADD bb INT(11) NOT NULL COMMENT "测试";

5.3.2、修改列

ALTER TABLE 表名 MODIFY COLUMN 字段名 数据类型(长度)  COMMENT "注释";  

5.3.3、删除列

ALTER TABLE 表名 DROP COLUMN 字段名;

5.4、查看所有表

show tables;

5.5、查看表结构

DESC 表名;

在这里插入图片描述

5.6、查看表详细信息

存储引擎,以及表创建时间等等。

SHOW TABLE STATUS LIKE '表名' \G

5.7、重命名表

两种都可以。

ALTER TABLE 表名 RENAME TO 新表名;

RENAME TABLE 表名 TO 新表名;

5.8、删除表

DROP TABLE table_name ;

5.9、复制表

步骤一:

获取要复制的数据表的完整结构。

mysql> SHOW CREATE TABLE runoob_tbl \G;
*************************** 1. row ***************************
       Table: runoob_tbl
Create Table: CREATE TABLE `runoob_tbl` (
  `runoob_id` int(11) NOT NULL auto_increment,
  `runoob_title` varchar(100) NOT NULL default '',
  `runoob_author` varchar(40) NOT NULL default '',
  `submission_date` date default NULL,
  PRIMARY KEY  (`runoob_id`),
  UNIQUE KEY `AUTHOR_INDEX` (`runoob_author`)
) ENGINE=InnoDB 
1 row in set (0.00 sec)

ERROR:
No query specified

步骤二:

修改SQL语句的数据表名,并执行SQL语句。

mysql> CREATE TABLE `clone_tbl` (
  -> `runoob_id` int(11) NOT NULL auto_increment,
  -> `runoob_title` varchar(100) NOT NULL default '',
  -> `runoob_author` varchar(40) NOT NULL default '',
  -> `submission_date` date default NULL,
  -> PRIMARY KEY  (`runoob_id`),
  -> UNIQUE KEY `AUTHOR_INDEX` (`runoob_author`)
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (1.80 sec)

步骤三:

执行完第二步骤后,你将在数据库中创建新的克隆表 clone_tbl。 如果你想拷贝数据表的数据你可以使用

INSERT INTO... SELECT 语句来实现。
mysql> INSERT INTO clone_tbl (runoob_id,
    ->                        runoob_title,
    ->                        runoob_author,
    ->                        submission_date)
    -> SELECT runoob_id,runoob_title,
    ->        runoob_author,submission_date
    -> FROM runoob_tbl;
Query OK, 3 rows affected (0.07 sec)
Records: 3  Duplicates: 0  Warnings: 0

6、数据操作

6.1、查询数据

6.1.1、查询多列

AS不分大小写,并且起别名的时候也可以省略。

SELECT 列名 AS 別名, 列名 AS 別名 FROM 表名;

6.1.2、检索唯一值

使用 DISTINCT 关键字,查询字段 pwd 的唯一值。

SELECT DISTINCT pwd FROM user;

6.1.3、分页查询

两种写法:

SELECT * FROM table  LIMIT offset, rows;
SELECT * FROM table  LIMIT rows OFFSET offset

offset:行开始的行的索引。0表示从第1行 开始显示(包括第1行),以此类推。

rows:数据显示的条数。

SELECT * FROM table LIMIT 5;    -- 检索前5条数据
--相当于
SELECT * from table LIMIT 0,5;    -- 从第0行开始检索5条数据
--相当于
SELECT * FROM table LIMIT 5 OFFSET 0;    -- 从第0行开始检索5条数据,注意这里的LIMIT的5指代的是数量

面试的时候最经常问的一道题:
mysql取200 - 300的数据怎么取?
首先得搞明白是否包含200条,和第300条

包括:select * from table limit 199,101
不包含: select * from table limit 200,99

6.1.4、排序

ASC升序,DESC降序

ORDER 默认升序排序。指定 DESC 关键字进行降序(从Z到 A)排序。

在指定一条 ORDER BY 子句时,应该保证它是 SELECT 语句中最后一条子句。排序字段可以同时多个,使用逗号分割开就可以了。

SELECT * FROM TABLE_NAME ORDER BY 列名, 列名 DESC;

6.1.5、注释

-- 单行注释
# 单行注释
/* 多行注释 */

6.2、插入数据

INSERT INTO table_name ( field1, field2,...fieldN )
                       VALUES
                       ( value1, value2,...valueN );

如果数据是字符型,必须使用单引号或者双引号,如:“value”。

6.2.1、插入完整行

插入完整行可以省略表后面的属性名称,但是值也必须得和desc查询出来的属性顺序一致,否则类型不一致或者值就会插入错误。

mysql> desc user;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| uid   | int(11)     | NO   | PRI | NULL    | auto_increment |
| uname | varchar(30) | NO   | UNI | NULL    |                |
| pwd   | varchar(30) | NO   |     | NULL    |                |
| age   | int(11)     | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

mysql>  insert into user values (10,"123","123",22);
Query OK, 1 row affected (0.01 sec)

6.2.2、插入部分行

insert into user(uname,pwd) value ("3323","123");

6.2.3、批量插入数据

insert into user(uname,pwd) value ("3323","123"),
								  ("4444","555");

6.2.4、从一个表复制到另一个表

CREATE TABLE user_test AS SELECT * FROM user;
  • 任何 SELECT 选项和子句都可以使用,包括 WHEREGROUP BY
  • 可利用联结从多个表插入数据。
  • 不管从多少个表中检索数据,数据都只能插入到一个表中。

主要用途:它是试验新 SQL 语句前进行表复制的很好工具。先进行复制,可在复制的数据上测试 SQL 代码,而不会影响实际的数据。

6.3、更新数据

可以一下子更改多列的值,也可以更改单列的值。多列的时候,使用逗号分割即可。

UPDATE 表名 SET 列名=新的值, 列名=新的值;

你可以同时更新一个或多个字段。
你可以在 WHERE 子句中指定任何条件。
你可以在一个单独表中同时更新数据。
当你需要更新数据表中指定行的数据时 WHERE 子句是非常有用的。

以下实例将更新数据表中 runoob_id 为 3 的 runoob_title 字段值:

UPDATE runoob_tbl SET runoob_title='学习 C++' WHERE runoob_id=3;

6.4、删除数据

DELETE FROM 表名 WHERE name = '张三';

如果没有指定 WHERE 子句,MySQL 表中的所有记录将被删除。
你可以在 WHERE 子句中指定任何条件
您可以在单个表中一次性删除记录。
当你想删除数据表中指定的记录时 WHERE 子句是非常有用的。

如果想从表中删除所有行,推荐使用 TRUNCATE TABLE 语句,它完成相同的工作,而速度更快(因为不记录数据的变动)。

但要注意: TRUNCATE 属于数据定义语言( DDL ),且 TRUNCATE 命令执行后无法回滚,使用 TRUNCATE 命令之前最好对当前表中的数据做备份。

TRUNCATE TABLE 表名;

7、where条件过滤

7.1、WHERE 子句操作符

在这里插入图片描述

7.2、范围值检查

使用 WHERE 关键字和 BETWEEN AND 进行范围值检查

查询 字段 age 中 >=5 并且 <= 10 的数据。

SELECT age   FROM user WHERE age BETWEEN 5 AND 10;

7.3、LIKE模糊查询

% 表示任何字符出现任意次数。例如,为了找出所有以 开始的 name

SELECT name, age FROM user WHERE name LIKE '路%';

通配符 _ 的用途与 % 一样也是匹配任意字符,但它只匹配单个字符,而不是多个字符

SELECT name, age  FROM user WHERE name LIKE '乌_普';

7.4、NULL 值处理

  • IS NULL: 当列的值是 NULL,此运算符返回 true。
  • IS NOT NULL: 当列的值不为 NULL, 运算符返回 true。

关于 NULL 的条件比较运算是比较特殊的。你不能使用 = NULL 或 != NULL 在列中查找 NULL 值 。= null和 != null运算符是不起作用的。

示例:查询name字段为空的数据

SELECT *  FROM user WHERE name IS NULL;

7.5、逻辑运算符

逻辑运算符用来判断表达式的真假。如果表达式是真,结果返回 1。如果表达式是假,结果返回 0。
在这里插入图片描述
AND 的优先级比 OR 要高,优先级高低 () 、 AND 、 OR。在使用的过程中要注意各个优先级的影响。

mysql> SELECT name, age
    -> FROM user
    -> WHERE(name = '索隆' OR name = '路飞')
    -> AND age >= 18;

7.6、IN 操作符

IN代表包含,NOT IN代表不包含在内。

mysql> SELECT name, age
    -> FROM user
    -> WHERE name IN ('索隆', '路飞')
mysql> SELECT name
    -> FROM user
    -> WHERE name NOT IN ('索隆', '路飞')

7.7、EXISTS操作符

他可以完成和in一样的功能。

#对B查询涉及id,使用索引,故B表效率高,可用大表 -->外小内大
select * from A where exists (select * from B where A.id=B.id);

#对A查询涉及id,使用索引,故A表效率高,可用大表 -->外大内小
select * from A where A.id in (select id from B);

1、exists是对外表做loop循环,每次loop循环再对内表(子查询)进行查询,那么因为对内表的查询使用的索引(内表效率高,故可用大表),而外表有多大都需要遍历,不可避免(尽量用小表),故内表大的使用exists,可加快效率;

2、in是把外表和内表做hash连接,先查询内表,再把内表结果与外表匹配,对外表使用索引(外表效率高,可用大表),而内表多大都需要查询,不可避免,故外表大的使用in,可加快效率。

3、如果用not in ,则是内外表都全表扫描,无索引,效率低,可考虑使用not exists,也可使用A left join B on A.id=B.id where B.id is null 进行优化。

8、关联

可以在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询。

JOIN 按照功能大致分为如下四类:

INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录
CROSS JOIN交叉连接(最后得到迪卡尔乘积)。

测试数据:

CREATE TABLE t1 (
    id INT PRIMARY KEY,
    pattern VARCHAR(50) NOT NULL
);
CREATE TABLE t2 (
    id VARCHAR(50) PRIMARY KEY,
    pattern VARCHAR(50) NOT NULL
);
INSERT INTO t1(id, pattern)
VALUES(1,'Divot'),
      (2,'Brick'),
      (3,'Grid');

INSERT INTO t2(id, pattern)
VALUES('A','Brick'),
      ('B','Grid'),
      ('C','Diamond');

8.1、交叉连接(CROSS JOIN)

SELECT 
    t1.id, t2.id
FROM
    t1
CROSS JOIN t2;

这两种写法得出来的结果是一样的。

SELECT
	t1.id,
	t2.id 
FROM
	t1,
	t2 

运行结果:

8.2、内连接(INNER JOIN)

有时候我们会看到单独用一个join的,他默认其实就相当于是INNER JOIN。

SELECT 
    t1.id, t2.id
FROM
    t1
        INNER JOIN
    t2 ON t1.pattern = t2.pattern;

这两种写法得出来的结果是一样的。

SELECT
	t1.id,
	t2.id 
FROM
	t1,
	t2 
WHERE
	t1.pattern = t2.pattern;

运行结果:

8.3、左连接(LEFT JOIN)

SELECT 
    t1.id, t2.id
FROM
    t1
        LEFT JOIN
    t2 ON t1.pattern = t2.pattern;

运行结果:

8.4、右连接(RIGHT JOIN)

SELECT 
    t1.id, t2.id
FROM
    t1
        right JOIN
    t2 ON t1.pattern = t2.pattern

运行结果:

9、UNION 操作符

union 和 union all 就是把两条或多条查询 SQL 的结果经行合并;

union 合并后的结果会进行去重。
union all 合并后的结果不会进行去重。

语法:

select  column,......from table1
union [all]
select  column,...... from table2

测试数据:

-- 创建表  学生表
CREATE TABLE `tab_student` (
	`stu_id` VARCHAR (16) NOT NULL COMMENT '学号',
	`stu_name` VARCHAR (20) NOT NULL COMMENT '学生姓名',
	`stu_sex` VARCHAR(1) NOT NULL COMMENT '学生性别',
	PRIMARY KEY (`stu_id`)
) COMMENT = '学生表' ENGINE = INNODB;

-- 插入需要练习的数据
INSERT INTO tab_student VALUES
(1,'依依','女'),
(2,'小二','女'),
(3,'张三','男'),
(4,'李四','男');

9.1、union

SELECT * FROM tab_student WHERE stu_id = 1
UNION
SELECT * FROM tab_student WHERE stu_sex = '女';

在这里插入图片描述

9.2、union all

SELECT * FROM tab_student WHERE stu_id = 1
UNION ALL
SELECT * FROM tab_student WHERE stu_sex = '女';

在这里插入图片描述

10、数据分组

如果分组列中包含具有 NULL 值的行,则 NULL 将作为一个分组返回。 如果列中有多行 NULL 值,它们将分为一组。

除聚集计算语句外,SELECT 语句中的每一列都必须在 GROUP BY 子句 中给出。

测试数据:

CREATE TABLE `employee_tbl` (
  `id` int(11) NOT NULL,
  `name` char(10) NOT NULL DEFAULT '',
  `date` datetime NOT NULL,
  `signin` tinyint(4) NOT NULL DEFAULT '0' COMMENT '登录次数',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `employee_tbl`
VALUES
	( '1', '小明', '2016-04-22 15:25:33', '1' ),
	( '2', '小王', '2016-04-20 15:25:47', '3' ),
	( '3', '小丽', '2016-04-19 15:26:02', '2' ),
	( '4', '小王', '2016-04-07 15:26:14', '4' ),
	( '5', '小明', '2016-04-11 15:26:40', '4' ),
	( '6', '小明', '2016-04-04 15:26:54', '2' );

10.1、group by

这张表我们可以把它当做订单表,一条数据就是一个订单,然后现在我要统计每个人下了几张订单,这时候就需要分组查询。

SELECT name, COUNT(*) FROM   employee_tbl GROUP BY name;

有了那个数据我还不满足,我还想查到下单最多的人。

SELECT
	* 
FROM
	( SELECT NAME, COUNT(*) aa FROM employee_tbl GROUP BY NAME ) T1 
ORDER BY
	T1.aa DESC 
	LIMIT 1

查询结果:
在这里插入图片描述

10.2、having

然后我还想统计出,下订单超过两条的人。

SELECT NAME,
	COUNT(*) 
FROM
	employee_tbl 
GROUP BY
NAME 
HAVING
	count(*) > 2;

执行结果:
在这里插入图片描述
WHEREHAVING 的主要区别:

WHERE 在数据分组前进行过滤,HAVING 在数据分组后进行过滤。
在这里插入图片描述
在现实开发当中经常会出现要删除重复数据的场景,可以通过这段sql来进行查询出重复数据。

SELECT
	列名 1, count( 1AS count 
FROM
	表名 
GROUP BY
	列名 1 
HAVING
	count >1

10.3、使用 WITH ROLLUP

WITH ROLLUP 可以实现在分组统计数据基础上再进行相同的统计(SUM,AVG,COUNT…)。

他会多出一行,实现在分组统计数据基础上再进行相同的统计,这里用的sum,就相当于分完组之后将数据加到一块,然后另开一行数据进行展示。

SELECT name, SUM(signin) as signin_count FROM  employee_tbl GROUP BY name WITH ROLLUP;

查询结果:

11、MySQL 正则表达式

MySQL中使用 REGEXP 操作符来进行正则表达式匹配。
在这里插入图片描述
查找name字段中以’st’为开头的所有数据:

mysql> SELECT name FROM person_tbl WHERE name REGEXP '^st';

查找name字段中以’ok’为结尾的所有数据:

mysql> SELECT name FROM person_tbl WHERE name REGEXP 'ok$';

查找name字段中包含’mar’字符串的所有数据:

mysql> SELECT name FROM person_tbl WHERE name REGEXP 'mar';

查找name字段中以元音字符开头或以’ok’字符串结尾的所有数据:

mysql> SELECT name FROM person_tbl WHERE name REGEXP '^[aeiou]|ok$';

12、常用函数

12.1、MySQL 字符串函数

在这里插入图片描述

12.2、MySQL 数字函数

ABS(x):返回x的绝对值
BIN(x):返回x的二进制(OCT返回八进制,HEX返回十六进制)
CEILING(x):返回大于x的最小整数值
EXP(x):返回值e(自然对数的底)的x次方
FLOOR(x):返回小于x的最大整数值
GREATEST(x1,x2,...,xn):返回集合中最大的值
LEAST(x1,x2,...,xn):返回集合中最小的值
LN(x):返回x的自然对数
LOG(x,y):返回x的以y为底的对数
MOD(x,y):返回x/y的模(余数)
PI():返回pi的值(圆周率)
RAND():返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。
ROUND(x,y):返回参数x的四舍五入的有y位小数的值
SIGN(x):返回代表数字x的符号的值
SQRT(x):返回一个数的平方根
TRUNCATE(x,y):返回数字x截短为y位小数的结果

12.3、MySQL 日期函数

在这里插入图片描述

12.4、MySQL 聚合函数

AVG(col):返回指定列的平均值
COUNT(col):返回指定列中非NULL值的个数
MIN(col):返回指定列的最小值
MAX(col):返回指定列的最大值
SUM(col):返回指定列的所有值之和
GROUP_CONCAT(col) :返回由属于一组的列值连接组合而成的结果
这个示例当中,我group by的name,通过这个函数,我们可以获取到分组完的其他列值。
示例:select GROUP_CONCAT(id) from goods GROUP BY name

12.5、MySQL 控制流函数

MySQL有4个函数是用来进行条件操作的,这些函数可以实现SQL的条件逻辑,允许开发者将一些应用程序业务逻辑转换到数据库后台。

CASE [test] WHEN[val1] THEN [result]…ELSE [default]END 如果test和valN相等,则返回resultN,否则返回default
示例:id=2的时候返回2,否则返回3

SELECT
CASE
		id 
		WHEN 2 THEN
		2 
		ELSE 3 
	END 
	FROM
		goods;

IF(test,t,f):如果test是真,返回t;否则返回f
IFNULL(arg1,arg2): 如果arg1不是空,返回arg1,否则返回arg2
NULLIF(arg1,arg2): 如果arg1=arg2返回NULL;否则返回arg1

12.6、MySQL 类型转化函数

为了进行数据类型转化,MySQL提供了CAST()函数,它可以把一个值转化为指定的数据类型。

语法:CAST (expression AS data_type)

expression:任何有效的SQServer表达式。
AS:用于分隔两个参数,在AS之前的是要处理的数据,在AS之后是要转换的数据类型。
data_type:目标系统所提供的数据类型,包括bigint和sql_variant,不能使用用户定义的数据类型。

可以转换的类型是有限制的。这个类型可以是以下值其中的一个:

二进制,同带binary前缀的效果 : BINARY
字符型,可带参数 : CHAR()
日期 : DATE
时间: TIME
日期时间型 : DATETIME
浮点数 : DECIMAL
整数 : SIGNED
无符号整数 : UNSIGNED

示例1:日期转数字

SELECT CAST(NOW() AS SIGNED);

示例2:保留小数2位

SELECT CAST('9.5' AS decimal(10,2))

结果:9.50(10是总的数字位数,包括小数点左边和右边位数的总和。而2是小数点右边的位数)

12.7、MySQL 系统信息函数

DATABASE():返回当前数据库名
BENCHMARK(count,expr):将表达式expr重复运行count次
CONNECTION_ID():返回当前客户的连接ID
FOUND_ROWS():返回最后一个SELECT查询进行检索的总行数
USER()SYSTEM_USER():返回当前登陆用户名
VERSION():返回MySQL服务器的版本

13、mysql存储引擎

数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。

因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(Table Type,即存储和操作此表的类型)。在创建表的时候可以指定存储引擎类型。

如创建一个InnoDB类型的表:

CREATE TABLE IF NOT EXISTS `runoob_tbl`(
   `runoob_id` INT UNSIGNED AUTO_INCREMENT COMMENT '注释',
   PRIMARY KEY ( `runoob_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

13.1、查看默认存储引擎

这个命令可以查看数据库默认的存储引擎。

SHOW ENGINES;

查看结果:

13.2、存储引擎

MyISAM:MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事物和外键。

InnoDB :5.5版本后Mysql的默认数据库,支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。

Memory :所有数据置于内存的存储引擎,拥有极高的插入,更新和查询效率。但是会占用和数据量成正比的内存空间。并且其内容会在Mysql重新启动时丢失,MEMORY存储引擎默认使用哈希(HASH)索引。

Archive:非常适合存储大量的独立的,作为历史记录的数据。因为它们不经常被读取。Archive拥有高效的插入速度,但其对查询的支持相对较差

Federated:将不同的Mysql服务器联合起来,逻辑上组成一个完整的数据库。非常适合分布式应用

CSV:逻辑上由逗号分割数据的存储引擎。它会在数据库子目录里为每个数据表创建一个.CSV文件。这是一种普通文本文件,每个数据行占用一个文本行。CSV存储引擎不支持索引。—我记得这个就是用来存储索引的…?

BlackHole :黑洞引擎,该存储引擎支持事务,而且支持mvcc的行级锁,写入这种引擎表中的任何数据都会消失,主要用于做日志记录或同步归档的中继存储,这个存储引擎除非有特别目的,否则不适合使用。

PERFORMANCE_SCHEMA存储引擎:主要用于收集数据库服务器性能参数。这种引擎提供以下功能:提供进程等待的详细信息,包括锁、互斥变量、文件信息;

14、MySQL 索引

拿汉语字典的目录页(索引)打比方,我们可以按拼音、笔画、偏旁部首等排序的目录(索引)快速查找到需要的字。

索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。

创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件)。

实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。

索引也会有它的缺点:建立索引会占用磁盘空间的索引文件,虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。

14.1、索引类型

14.1.1、NORMAL 普通索引

表示普通索引,大多数情况下都可以使用,所谓的组合索引,也是普通索引类型当中的,无非是多个列组成的一个索引。

14.1.2、UNIQUE 唯一索引

唯一索引列值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一。事实上,在许多场合,创建唯一索引的目的往往不是提高访问速度,而是为了避免数据出现重复。

14.1.3、FULLTEXT 全文索引

表示全文收索,在检索长文本的时候,效果最好,短文本建议使用Index,但是在检索的时候数据量比较大的时候,先将数据放入一个没有全局索引的表中,然后在用Create Index创建的Full Text索引,要比先为一张表建立Full Text然后在写入数据要快的很多

全文索引只能作用在 CHARVARCHARTEXT、类型的字段上。全文索引只能在存储引擎为MYISAM的表中创建。

14.1.4、SPATIAL 空间索引

空间索引是对空间数据类型的字段建立的索引,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。MYSQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类型的语法创建空间索引。创建空间索引的列,必须将其声明为NOT NULL,空间索引只能在存储引擎为MYISAM的表中创建

14.2、创建索引

注意:创建普通索引不写索引类型。

创建方式一:

CREATE 索引类型 INDEX 索引名称 ON 表名 (列名);

示例:创建唯一索引,这里我设置了两个列作为索引,也就是我们常说的组合索引。

CREATE UNIQUE INDEX index1 ON runoob_tbl (runoob_title,runoob_author)

创建方式二:修改表结构的方式添加索引

ALTER table 表名 ADD 索引类型 INDEX 索引名称(列名);

示例:

ALTER table runoob_tbl ADD SPATIAL INDEX index4(cc);

14.3、删除索引

DROP INDEX indexName ON table_name; 

14.4、索引方法

我用的navcat客户端,随便点击一张表,然后点击设计表,打开索引,就可以看见创建索引的时候,我们是可以指定索引方法的,这里一共有两种。

不同的引擎对于索引有不同的支持:Innodb和MyISAM默认的索引是Btree索引;而Mermory默认的索引是Hash索引。

14.4.1、btree索引

BTree索引是最常用的mysql数据库索引算法,因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量,例如:

select * from user where name like ‘jack%;
select * from user where name like ‘jac%k%;

通配符开头,或者没有使用常量,则不会使用索引,例如:

select * from user where name like%jack’;
select * from user where name like simply_name;

14.4.2、hash索引

所谓Hash索引,当我们要给某张表某列增加索引时,将这张表的这一列进行哈希算法计算,得到哈希值,排序在哈希数组上。所以Hash索引可以一次定位,其效率很高,而Btree索引需要经过多次的磁盘IO,但是innodb和myisam之所以没有采用它,是因为它存在着好多缺点:

1、Hash索引比较的是经过Hash计算的值,所以只能进行等式比较,不能用于范围查询
2、每次都要全表扫描
3、由于哈希值是按照顺序排列的,但是哈希值映射的真正数据在哈希表中就不一定按照顺序排列,所以无法利用Hash索引来加速任何排序操作。
4、不能用部分索引键来搜索,因为组合索引在计算哈希值的时候是一起计算的。
5、当哈希值大量重复且数据量非常大时,其检索效率并没有Btree索引高的。

Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引。

15、mysql变量分类

15.1、变量分类与关系

MySQL中的变量分为全局变量、会话变量、用户变量和局部变量。

MySQL服务器维护了许多系统变量来控制其运行的行为,这些变量有些是默认编译到软件中的,有些是可以通过外部配置文件来配置覆盖的,如果想查询自编译的内置变量和从文件中可以读取覆盖的变量可以通过以下命令在cmd窗口执行查询:

mysqld --verbose --help

如果想只看自编译的内置变量可以使用命令:

mysqld --no-defaults --verbose --help

全局变量:首先MySQL服务器启动时会使用其软件内置的变量(俗称写死在代码中的)和配置文件中的变量(如果允许,是可以覆盖源代码中的默认值的)来初始化整个MySQL服务器的运行环境,这些变量通常就是我们所说的全局变量,这些在内存中的全局变量有些是可以修改的。

会话变量:当有客户端连接到MySQL服务器的时候,MySQL服务器会将这些全局变量的大部分复制一份作为这个连接客户端的会话变量,这些会话变量与客户端连接绑定,连接的客户端可以修改其中允许修改的变量,但是当连接断开时这些会话变量全部消失,重新连接时会从全局变量中重新复制一份。

用户变量:用户变量其实就是用户自定义变量,当客户端连接上MySQL服务器之后就可以自己定义一些变量,这些变量在整个连接过程中有效,当连接断开时,这些用户变量消失。

局部变量:通常由DECLARE 关键字来定义,经常出现在存储过程中。

15.2、变量的修改

先说全局变量有很多是可以动态调整的,也就是说可以在MySQL服务器运行期间通过 SET 命令修改全局变量,而不需要重新启动 MySQL 服务,但是这种方法在修改大部分变量的时候都需要超级权限,比如root账户。

相比之下会话对变量修改的要求要低的多,因为修改会话变量通常只会影响当前连接,但是有个别一些变量是例外的,修改它们也需要较高的权限,比如 binlog_format 和 sql_log_bin,因为设置这些变量的值将影响当前会话的二进制日志记录,也有可能对服务器复制和备份的完整性产生更广泛的影响。

至于用户变量和局部变量,听名字就知道,这些变量的生杀大权完全掌握在自己手中,想改就改,完全不需要理会什么权限,它的定义和使用全都由用户自己掌握。

15.3、全局变量查询与设置

全局变量:这些变量来源于软件自编译、配置文件中、以及启动参数中指定的变量,其中大部分是可以由root用户通过 SET 命令直接在运行时来修改的,一旦 MySQL 服务器重新启动,所有修改都被还原。如果修改了配置文件,想恢复最初的设置,只需要将配置文件还原,重新启动 MySQL 服务器,一切都可以恢复原来的样子。

查询

show global variables;

一般不会这么用,这样查简直太多了,大概有500多个,通常会加个like控制过滤条件:

show global variables like 'sql%';

还有一种查询方法就是通过select语句:

select @@global.sql_mode;

当一个全局变量不存在会话变量副本时也可以这样

select @@sql_auto_is_null;
  1. select@@变量名这种形式默认取的是会话变量,如果查询的会话变量不存在就会获取全局变量,比如@@max_connections
  2. 但是SET操作的时候,set @@变量名=xxx 总是操作的会话变量,如果会话变量不存在就会报错

设置

set global sql_mode='';
set @@global.sql_mode='';

15.4、会话变量查询与设置

这些变量基本来自于全局变量的复制,与客户端连接有关,无论怎样修改,当连接断开后,一切都会还原,下次连接时又是一次新的开始。

查询

show session variables;

一般不会这么用,这样查简直太多了,大概有500多个,通常会加个like控制过滤条件:

show session variables like 'sql%';

查询特定的会话变量,以下三种都可以:

select @@session.sql_mode;
select @@local.sql_mode;
select @@sql_mode;

设置

set session sql_mode = '';
set local sql_mode = '';
set @@session.sql_mode = '';
set @@local.sql_mode = '';
set @@sql_mode = '';
set sql_mode = '';

15.5、用戶变量查询与设置

用户变量就是用户自己定义的变量,也是在连接断开时失效,定义和使用相比会话变量来说简单许多。

查询:

select @count;

设置

set @count=1; 
set @sum:=0;

也可以使用select into语句来设置值,比如:

select count(id) into @count from items where price < 99;

15.6、局部变量查询与设置

局部变量通常出现在存储过程中,用于中间计算结果,交换数据等等,当存储过程执行完,变量的生命周期也就结束了。

查询

declare count int(4); 
select count;

设置

declare count int(4); 
declare sum int(4); 
set count=1; 
set sum:=0;

也可以使用select into语句来设置值,比如:

declare count int(4); 
select count(id) into count from items where price < 99;

16、管理事务处理

事务处理(transaction processing)是一种机制, 用来管理必须成批执行的SQL` 操作,保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们要么完全执行,要么完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表;如果发生错误,则进行回退(撤销),将数据库恢复到某个已知且安全的状态,以此来维护数据库的完整性。

16.1、事务处理术语

事务(transaction):指一组 SQL 语句;
回退(rollback):指撤销指定 SQL 语句的过程;
提交(commit):指将未存储的 SQL 语句结果写入数据库表;
保留点(savepoint):指事务处理中设置的临时占位符(placeholder), 可以对它发布回退(与回退整个事务处理不同)。

事务处理用来管理 INSERTUPDATEDELETE 语句。不能回退 SELECT 语句(回退 SELECT 语句也没有必要),也不能回退 CREATEDROP 操 作。事务处理中可以使用这些语句,但进行回退时,这些操作也不撤销。

16.2、事务的四大特性

原子性: 一个事务( transaction )中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚( Rollback )到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性: 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

隔离性: 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交( Read uncommitted )、读提交( read committed )、可重复读( repeatable read )和串行化( Serializable)。

持久性: 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

16.3、控制事务处理

在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。因此要显式地开启一个事务务须使用命令 BEGIN 或 START TRANSACTION,或者执行命令 SET AUTOCOMMIT=0,用来禁止使用当前会话的自动提交。

#显式地开启一个事务两种方式:
BEGIN;
START TRANSACTION;

#提交事务两种方式,使已对数据库进行的所有修改成为永久性的:
COMMIT;
COMMIT WORK;

#回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
ROLLBACK;
ROLLBACK WORK;

#允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
SAVEPOINT 保存点名称;

#删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
RELEASE SAVEPOINT 保存点名称;

#把事务回滚到标记点;
ROLLBACK TO 保存点名称;

#用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有 READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交,项目中常用)、REPEATABLE READ(可重复读,Mysql 默认隔离级别) 和 SERIALIZABLE(串行化)。
SET TRANSACTION;

简单示例:

mysql> use test;

mysql> CREATE TABLE transaction_test(id int(5)) ENGINE = INNODB;  # 创建数据表

mysql> SELECT * FROM transaction_test;
Empty set (0.01 sec)

mysql> BEGIN;  # 开始事务

mysql> INSERT INTO runoob_transaction_test VALUE(1);

mysql> INSERT INTO runoob_transaction_test VALUE(2);

mysql> COMMIT; # 提交事务

mysql>  SELECT * FROM transaction_test;
+------+
| id   |
+------+
| 1    |
| 2    |
+------+



mysql> BEGIN;    # 开始事务

mysql> INSERT INTO transaction_test VALUES(3);

mysql> SAVEPOINT first_insert;    # 声明一个保存点

mysql> INSERT INTO transaction_test VALUES(4);

mysql> SAVEPOINT second_insert;    # 声明一个保存点

mysql> INSERT INTO transaction_test VALUES(5);

mysql> ROLLBACK TO second_insert;    # 回滚到 second_insert 保存点

mysql> SELECT * FROM transaction_test;    # 因为回滚所以数据没有插入
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
|    4 |
+------+

mysql> ROLLBACK TO first_insert;

mysql> SELECT * FROM transaction_test;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+

mysql> COMMIT;    # 执行 COMMIT 或 ROLLBACK 后保存点自动释放

16.4、事务隔离级别

在mysql当中有一个变量是可以设置事务隔离级别的。
早期版本的mysql中用的变量名称是tx_isolation,5.7.20版本之后,用的是transaction_isolation。

# 可以先通过这个命令来看看隔离级别名称是什么
show variables like 't%_isolation';

# 查看默认事务隔离级别(session)
select @@transaction_isolation;

# 查看当前会话的隔离级别
select @@session.transaction_isolation;

# 查看全局的事务隔离级别
select @@global.transaction_isolation;

16.4.1 读未提交

对于两个事务T1与T2,T1读取了已经被T2更新但是还没有提交的字段之后,若此时T2回滚,T1读取的内容就是临时并且无效的。

打开两个Mysql客户端,分别执行下面操作,查询当前会话的隔离级别(默认 REPEATABLE READ )。修改当前会话隔离级别为( READ UNCOMMITTED )。全局事务隔离级别仍然为 REPEATABLE READ

mysql> SELECT @@session.transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;    # 修改会话隔离级别

mysql> SELECT @@session.transaction_isolation;    # 当前会话隔离级别已修改
+---------------------------------+
| @@session.transaction_isolation |
+---------------------------------+
| READ-UNCOMMITTED                |
+---------------------------------+

mysql> SELECT @@global.transaction_isolation;    # 全局事务隔离级别未修改
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ                |
+--------------------------------+

之后黑框用来做更新,白框用来查询。

由于黑框的 ④ 回滚,白色背景的客户端中 ③ 读取的数据就是临时并且无效的。即脏读。

16.4.2 不可重复读

对于两个事务T1和T2,T1读取了一个字段,然后T2更新了该字段并提交之后,当T1再次读取的时候,结果不一致的情况发生。

由于黑框的更新操作,白框的在同一事务内出现两次读取的结果不一致。

16.4.3 幻读

对于两个事务T1、T2,T1从表中读取数据,然后T2进行了INSERT操作并提交,当T1再次读取的时候,结果不一致的情况发生。

由于黑框的插入操作,白框在同一事务内出现两次读取的结果不一致。

16.4.4 串行化

对于两个事务T1、T2,T1从表中读取数据,但是并未结束事务,其他事务新增的时候就会失败。查询是可以查询的。

(1)打开一个客户端A,并设置当前事务模式为serializable,查询表account的初始值:

mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;
+------+--------+---------+
| id   | name   | balance |
+------+--------+---------+
|    1 | lilei  |   10000 |
|    2 | hanmei |   10000 |
|    3 | lucy   |   10000 |
|    4 | lily   |   10000 |
+------+--------+---------+
4 rows in set (0.00 sec)

(2)打开一个客户端B,并设置当前事务模式为serializable,插入一条记录报错,表被锁了插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。

mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values(5,'tom',0);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

17、MySQL 及 SQL 注入

假如登录的时候有一个验证用户的sql,让输入用户名。

 SELECT * FROM users WHERE name= #{name}

name = “Qadir’; DELETE FROM users;”;
假如程序没有对name的变量进行过滤,$name 中插入了我们不需要的SQL语句,将删除 users 表中的所有数据。不要小看这个问题,搞不好是非常严重的。

like查询时,如果用户输入的值有"“和”%",则会出现这种情况:用户本来只是想查询"abcd",查询结果中却有"abcd_"、“abcde”、“abcdf"等等;用户要查询"30%”(注:百分之三十)时也会出现问题。

防止SQL注入,我们需要注意以下几个要点:

  1. 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。
  2. 永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
  3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
  4. 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
  5. 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
  6. sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻击等。

18、存储过程

存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

18.1、查询存储过程

# 查看所有存储过程信息
SHOW PROCEDURE STATUS;
# 指定存储过程查看
SHOW CREATE PROCEDURE 存储过程名;

18.2、权限问题

有时候明明数据库有这个存储过程,但是call调用的时候却说找不到,这是因为用户没有存储过程权限。

// 查询权限
mysql> SHOW GRANTS FOR root@localhost;

// 刷新
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

// 赋值权限
mysql> GRANT ALL  PRIVILEGES ON test.*  TO 'root'@localhost;
Query OK, 0 rows affected (0.00 sec)

18.3、创建存储过程

MySQL中,创建存储过程的基本形式如下:

DELIMITER $$
CREATE
    PROCEDURE 存储过程名 (参数列表)
    BEGIN
		SQL语句代码块
    END$$
DELIMITER ; # DELIMITER后面一定要跟空格

DELIMITER $$;语句的作用是将MYSQL的结束符设置为$$,因为MYSQL默认的语句结束符为分号; ,存储过程中的SQL语句需要分号来结束,为了避免与存储过程中SQL语句结束符相冲突,需要使用DELIMITER 改变存储过程的结束符,并以"END $$"结束存储过程。存储过程定义完毕之后再使用DELIMITER ;恢复默认结束符。DELIMITER 也可以指定其他符号为结束符。

注意:当使用DELIMITER命令时,应该避免使用反斜杠(\)字符,因为反斜杠是MYSQL的转义字符!!!

DELIMITER 是分割符的意思,其实就是定义了一个语句执行的结束符;

默认情况下,delimiter是分号";"。在命令行客户端中,如果有一行命令以分号结束, 那么回车后,mysql将会执行该命令。如输入下面的语句

mysql> select * from stu; 

然后回车,那么MySQL将立即执行该语句。但有时候,不希望MySQL这么做。因为可能输入较多的语句,且语句中包含有分号。 默认情况下,不可能等到用户把这些语句全部输入完之后,再执行整段语句。因为mysql一遇到分号,它就要自动执行。 即,在语句之后为";“时,mysql解释器就要执行了。 这种情况下,就需要事先把delimiter换成其它符号,如//或$$等其他符号。 这样只有当$$出现之后,mysql解释器才会执行这段语句 。记得最后一个要将结束符修改回”;"。

18.4、存储过程参数

其中参数列表的形式如下:

[IN|OUT|INOUT] param_name type

param_name表示参数名称;type表示参数的类型,该类型可以是MYSQL数据库中的任意类型。

  • IN (输入参数):表示调用者向过程传入值(传入值可以是字面量或变量)
  • OUT (输出参数):表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
  • INOUT (输入输出参数):既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)

18.4.1、IN输入参数

由括号包围的参数列必须总是存在。如果没有参数,也该使用一个空参数列()。

每个参数默认都是一个IN参数。要指定为其它参数,可在参数名之前使用关键词 OUT或INOUT。

@p_in是一个用户变量,虽然 p_in 在存储过程中被修改,但并不影响 @p_in 的值,因为 in_test 只接受输入参数,并不输出参数,所以相当于在函数内改变,但并未将这个值输出给 @p_in

mysql> DELIMITER $$
mysql> DROP PROCEDURE IF EXISTS `in_test`$$
mysql> CREATE PROCEDURE in_test(IN p_in INT)
    -> BEGIN
    ->     SELECT p_in;    -- 第一次查询
    ->     SET p_in = 2;   -- 修改p_in的值
    ->     SELECT p_in;    -- 第二次查询
    -> END$$
    
mysql> DELIMITER ;

mysql> SET @p_in = 1;

mysql> CALL in_test(@p_in);
+------+
| p_in |
+------+
|    1 |
+------+

+------+
| p_in |
+------+
|    2 |
+------+

mysql> SELECT @p_in;
+-------+
| @p_in |
+-------+
|     1 |
+-------+

18.4.2、OUT输入参数

mysql> DELIMITER $$
mysql> DROP PROCEDURE IF EXISTS `out_test`$$
mysql> CREATE PROCEDURE out_test(OUT p_out INT)
    -> BEGIN
    ->     SELECT p_out;    -- 第一次查询
    ->     SET p_out = 2;   -- 修改p_out的值
    ->     SELECT p_out;    -- 第二次查询
    -> END$$

mysql> DELIMITER ;

mysql> SET @p_out = 1;

mysql> CALL out_test(@p_out);
+-------+
| p_out |
+-------+
|  NULL |
+-------+

+-------+
| p_out |
+-------+
|     2 |
+-------+

mysql> SELECT @p_out;
+--------+
| @p_out |
+--------+
|      2 |
+--------+

第一个返回结果为 NULL 是因为 OUT 是向调用者输出参数,不接收输入的参数,所以第一次查询时 p_out 还未赋值,所以是 NULL 。最后 @p_out 变量的值变为2是因为调用了 out_test 存储过程,输出参数,改变了 p_out 变量的值。

18.4.3、INOUT输入输出参数

mysql> DELIMITER $$
mysql> DROP PROCEDURE IF EXISTS `inout_test`$$
mysql> CREATE PROCEDURE inout_test(INOUT p_inout INT)
    -> BEGIN
    ->     SELECT p_inout;    -- 第一次查询
    ->     SET p_inout = 2;   -- 修改p_inout的值
    ->     SELECT p_inout;    -- 第一次查询
    -> END$$
    
mysql> DELIMITER ;

mysql> SET @p_inout = 1;

mysql> CALL inout_test(@p_inout);
+---------+
| p_inout |
+---------+
|       1 |
+---------+

+---------+
| p_inout |
+---------+
|       2 |
+---------+

mysql> SELECT @p_inout;
+----------+
| @p_inout |
+----------+
|        2 |
+----------+

调用 inout_test 存储过程,既接受了输入的参数,也输出参数, @p_inout 的值被改变。

18.5、删除存储过程

DROP PROCEDURE  IF  EXISTS  存储过程名;

这个语句被用来移除一个存储程序。不能在一个存储过程中删除另一个存储过程。

18.6、调用存储过程

CALL 存储过程名(参数列表);

18.7、存储过程体

存储过程体包含了在过程调用时必须执行的语句,例如:dml、ddl语句,if-then-else和while-do语句、声明变量的declare语句等。

过程体格式:以begin开始,以end结束(可嵌套)

18.8、定义变量

定义变量my_sql,数据类型为INT型,默认值为10。代码如下:

DECLARE  my_sql  INT  DEFAULT 10 ; 

DECLARE关键字是用来声明变量的;

my_sql参数是变量的名称,这里可以同时定义多个变量;

没有使用DEFAULT子句时,默认值为NULL。

使用说明:

局部变量只能在存储过程体的begin…end语句块中声明。
局部变量必须在存储过程体的开头处声明。
局部变量的作用范围仅限于声明它的begin…end语句块,其他语句块中的语句不可以使用它。
局部变量不同于用户变量,两者区别:局部变量声明时,在其前面没有使用@符号,并且它只能在begin…end语句块中使用;而用户变量在声明时,会在其名称前面使用@符号,同时已声明的用户变量存在于整个会话之中。

18.9、赋值变量

SELECT col_name[,...] INTO var_name[,...] table_expr;

col_name:要从数据库中查询的列字段名;

var_name:变量名,列字段名按照在列清单和变量清单中的位置对应,将查询得到的值赋给对应位置的变量;

table_expr:SELECT语句中的其余部分,包括可选的FROM子句和WHERE子句。

需要注意的是,在使用SELECT …INTO语句时,变量名不能和数据表中的字段名不能相同,否则会出错。

示例:

CREATE DEFINER=`root`@`localhost` PROCEDURE `test6`()
BEGIN
    // 定义一个变量
	DECLARE  NAME  VARCHAR(30); 
	// 将查询出来的数据赋值给变量
	select uname INTO `name` from user where uid = 1;
	// 通过变量进行其他业务查询。
	SELECT uid from user where uname = name;
END

19、游标

SQL 检索操作返回结果集,简单地使用 SELECT 语句,没有办法得到第一行、下一行或前 10 行。有时,需要在检索出来的行中前进或后退一行或多行,这就是游标的用途所在。游标(cursor)是一个存储在 DBMS 服务器上的数据库查询, 它不是一条 SELECT 语句,而是被该语句检索出来的结果集。在存储了 游标之后,应用程序可以根据需要滚动或浏览其中的数据。

19.1、使用游标

游标使用的步骤:

在使用游标前,必须声明(定义)它。这个过程实际上没有检索数据, 它只是定义要使用的 SELECT 语句和游标选项。
一旦声明,就必须打开游标以供使用。这个过程用前面定义的 SELECT 语句把数据实际检索出来。
对于填有数据的游标,根据需要取出(检索)各行。
在结束游标使用时,必须关闭游标,可能的话,释放游标。

声明游标后,可以根据需要频繁地 打开或关闭 游标。在游标打开时,可根据需要频繁地执行 操作。

注意:不像多数DBMS,MySQL游标只能用于存储过程(和函数)。

19.2、创建游标

使用 DECLARE 来创建游标,DECLARE 命名游标,并定义相应的 SELECT 语句,根据需要带 WHERE 和 其他子句。

下面的语句定义了名为 ordernumbers 的游标,使用了可以检索所有订单的 SELECT 语句。

DROP PROCEDURE IF EXISTS processorder;
CREATE PROCEDURE processorder()
BEGIN
	-- 定义游标
	DECLARE ordernumbers CURSOR
	FOR
	SELECT order_num FROM orders;
END;

这个存储过程中,使用 DECLARE 语句用来定义和命名游标。存储过程处理完成后,游标就消失(因为它局限于存储过程)。

19.3、使用游标数据

案例一:这个使用了loop循环。

DELIMITER $$

USE `test`$$

DROP PROCEDURE IF EXISTS `test2`$$

CREATE DEFINER = `root` @`localhost` PROCEDURE `test2` () 
BEGIN
  -- 定义局部变量
  DECLARE uid INT ;
  DECLARE count2 INT ;
  DECLARE done INT ;
  -- 定义游标
  DECLARE cur_test CURSOR FOR 
  SELECT 
    id,
    COUNT
  FROM
    test2 ;
  -- 若没有数据返回,程序继续,并将变量done设为1
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=true;
  -- 打开游标
  OPEN cur_test ;
  -- 定义loop
  posLoop :LOOP
    IF done = 1 
    -- LEAVE 语句主要用于跳出循环控制
    THEN LEAVE posLoop ;
    END IF ;
    -- 游标当中取值赋给变量
    FETCH cur_test INTO UID,count2 ;
    -- 这时候取到变量,可以进行业务处理。
    UPDATE 
      test2 
    SET
      COUNT= 2 
    WHERE id = UID ;
  -- 结束loop
  END LOOP posLoop ;
  -- 关闭游标
  CLOSE cur_test ;
END $$

DELIMITER ;

案例二:这个使用了REPEAT函数循环。

DELIMITER $$

USE `test`$$

DROP PROCEDURE IF EXISTS `test3`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `test3`()
BEGIN
  DECLARE uid INT ;
  DECLARE count2 INT ;
  DECLARE done INT ;
  DECLARE cur_test CURSOR FOR 
  SELECT 
    id,
    COUNT
  FROM
    test2 ;
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE ;
  OPEN cur_test ;
  REPEAT
    FETCH cur_test INTO UID,
    count2 ;
    UPDATE 
      test2 
    SET
      COUNT= 4 
    WHERE id = UID ;
  -- 判断REPEAT循环结束
    UNTIL done 
  END REPEAT ;
  CLOSE cur_test ;
END $$

DELIMITER ;
DECLARE CONTINUE HANDLER FOR SQLSTATE ‘02000SET done=true;

这条语句定义了一个 CONTINUE HANDLER,它是在条件出现时被执行的代码。这里,它指出当 SQLSTATE ‘02000’ 出现时,SET done=true。SQLSTATE '02000’是一个未找到条件,当 REPEAT 由于没有更多的行供循环而不能继续时,出现这个条件。

平时也可以这样写结束循环。这种的一般是特殊情况根据参数判断是否结束循环。而上面的案例是一定会循环完。

REPEAT
SET num := num + 1;
SET total := total + num;
UNTIL num >= 100 END REPEAT;

20、存储函数

存储函数和存储过程一样,都是在数据库中定义一些 SQL 语句的集合。存储函数可以通过 return 语句返回函数值,主要用于计算并返回一个值。而存储过程没有直接返回值,主要用于执行操作。

20.1、查询存储函数

# 查看所有存储函数信息
SHOW FUNCTION STATUS;
# 指定存储函数查看
SHOW CREATE FUNCTION 存储函数名;

20.2、创建存储函数

MySQL中,创建存储函数的基本形式如下:

DELIMITER $$
CREATE
    FUNCTION 存储函数名 (参数列表) RETURNS 返回值类型
    BEGIN
		SQL语句代码块,处理业务等等。。;
		RETURN 返回值;
    END$$
DELIMITER ;

使用 CREATE FUNCTION 创建查询 tb_student 表中某个学生姓名的函数,SQL 语句和执行过程如下:

mysql> USE test;
Database changed
mysql> DELIMITER //
mysql> CREATE FUNCTION func_student(id INT(11))
    -> RETURNS VARCHAR(20)
    -> COMMENT '查询某个学生的姓名'
    -> BEGIN
    -> RETURN(SELECT name FROM tb_student WHERE tb_student.id = id);
    -> END //
Query OK, 0 rows affected (0.10 sec)
mysql> DELIMITER ;

注意:存储函数初了返回值的时候可以出现不用变量赋值,其他查询一概需要变量赋值,否则报错。
例如:在返回之前有一个查询,但是查询出来的结果没有赋值变量,就会编译报错。

CREATE DEFINER=`root`@`localhost` FUNCTION `func_user`(id INT(11)) RETURNS varchar(20) CHARSET utf8
BEGIN
SELECT uname  FROM USER;
RETURN ( SELECT uname FROM USER u WHERE u.uID = id );
END

解决:

CREATE DEFINER=`root`@`localhost` FUNCTION `func_user`(id INT(11)) RETURNS varchar(20) CHARSET utf8
BEGIN
DECLARE  NAME  VARCHAR(30);
SELECT uname INTO `name` FROM USER;
RETURN ( SELECT uname FROM USER u WHERE u.uID = id );
END

20.3、调用存储函数

select 存储函数名(参数); 

示例:

select func_user(1); 

20.4、存储函数参数

同上方存储过程一样。

20.5、删除存储函数

DROP FUNCTION IF  EXISTS  存储函数名;

21、mysql定时任务

自MySQL5.1.6起,增加了一个非常有特色的功能-事件调度器(Event Scheduler),可以用做定时执行某些特定任务(例如:删除记录、对数据进行汇总、数据备份等等),来取代原先只能由操作系统的计划任务来执行的工作。更值得一提的是MySQL的事件调度器可以精确到每秒钟执行一个任务,而操作系统的计划任务(如:Linux的cron或Windows下的任务计划)只能精确到每分钟执行一次。对于一些对数据实时性要求比较高的应用(例如:股票、赔率、比分等)就非常适合。

事件调度器有时也可以称为临时触发器(temporal triggers),因为事件调度器是基于特定时间周期触发来执行某些任务,而触发器(Triggers)是基于某个表所产生的事件触发的,区别也就在这里。

21.1、准备工作

使用这个功能之前必须确保event_scheduler已开启。

可以使用这个命令进行查询是否开启:

show global variables like 'event_scheduler%';

OFF关的意思,ON开的意思。
在这里插入图片描述
开启的三种方式:开启之后就会变成ON。

SET GLOBAL event_scheduler = 1;
在配置my.cnf文件 中加上 event_scheduler = 1
 SET GLOBAL event_scheduler = ON;

注:将事件计划关闭:SET GLOBAL event_scheduler = 0;

21.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语句的子句:
在这里插入图片描述
在ON SCHEDULE子句中,参数schedule的值为一个AS子句,用于指定事件在某个时刻发生,其语法格式如下:

AT timestamp [+ INTERVAL interval] ...
  | EVERY interval
    [STARTS timestamp [+ INTERVAL interval] ...]
    [ENDS timestamp [+ INTERVAL interval] ...]

参数说明:

(1)timestamp:表示一个具体的时间点,后面加上一个时间间隔,表示在这个时间间隔后事件发生。

(2)EVERY子句:用于表示事件在指定时间区间内每隔多长时间发生一次,其中 STARTS子句用于指定开始时间;ENDS子句用于指定结束时间。

starts和ends都有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}

一些常用的时间间隔设置:
(1)5天后执行

ON SCHEDULE AT CURRENT_TIMESTAMP+INTERVAL 5 DAY

(2)指定时间段进行执行

ON SCHEDULE AT TIMESTAMP '2018-09-17 18:16:00'

(3)每隔5秒钟执行

ON SCHEDULE EVERY 5 SECOND

(4)每隔1分钟执行

ON SCHEDULE EVERY 1 MINUTE

(5)每天凌晨1点执行

ON SCHEDULE EVERY 1 DAY STARTS DATE_ADD(DATE_ADD(CURDATE(), INTERVAL 1 DAY), INTERVAL 1 HOUR)

(6)每个月的第一天凌晨1点执行

ON SCHEDULE EVERY 1 MONTH STARTS DATE_ADD(DATE_ADD(DATE_SUB(CURDATE(),INTERVAL DAY(CURDATE())-1 DAY),INTERVAL 1 MONTH),INTERVAL 1 HOUR)

(7)每 3 个月,从现在起一周后开始

ON SCHEDULE EVERY 3 MONTH STARTS CURRENT_TIMESTAMP + 1 WEEK

(8)每十二个小时,从现在起三十分钟后开始,并于现在起四个星期后结束

ON SCHEDULE EVERY 12 HOUR STARTS CURRENT_TIMESTAMP + INTERVAL 30 MINUTE ENDS CURRENT_TIMESTAMP + INTERVAL 4 WEEK

示例1:创建名称为event_user的事件,用于每隔5秒钟向数据表tb_user(用户信息表)中插入一条数据。

(1)首先创建tb_user(用户信息表)。

-- 创建用户信息表
CREATE TABLE IF NOT EXISTS tb_user
(
	id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号',
	name VARCHAR(30) NOT NULL COMMENT '用户姓名',
	create_time TIMESTAMP COMMENT '创建时间'
) COMMENT = '用户信息表';

(2)创建事件。

-- 创建事件
CREATE EVENT IF NOT EXISTS event_user
ON SCHEDULE EVERY 5 SECOND
ON COMPLETION PRESERVE
COMMENT '新增用户信息定时任务'
DO INSERT INTO tb_user(name,create_time) VALUES('pan_junbiao的博客',NOW());

示例2:创建一个事件,实现每个月的第一天凌晨1点统计一次已经注册的会员人数,并插入到统计表中。

(1)创建名称为p_total的存储过程,用于统计已经注册的会员人数,并插入到统计表tb_total中。

CREATE PROCEDURE p_total()
BEGIN
	DECLARE n_total INT default 0;
	SELECT COUNT(*) INTO n_total FROM db_database11.tb_user;
	INSERT INTO tb_total (userNumber,createtime) VALUES(n_total,NOW());
END;

(2)创建名称为e_autoTotal的事件,用于在每个月的第一天凌晨1点调用存储过程。

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();

21.3、查询事件

在MySQL中可以通过查询information_schema.events表,查看已创建的事件。其语句如下:

SELECT * FROM information_schema.events; 

21.4、修改事件

在MySQL 5.1及以后版本中,事件被创建之后,还可以使用ALTER EVENT语句修改其定义和相关属性。其语法如下:

ALTER
	[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;

ALTER EVENT语句与CREATE EVENT语句基本相同。另外ALTER EVENT语句还有一个用法就是让一个事件关闭或再次活动。

21.5、启动与关闭事件

另外ALTER EVENT语句还有一个用法就是让一个事件关闭或再次活动。
示例:启动名称为event_user的事件。

ALTER EVENT event_user ENABLE;

示例:关闭名称为event_user的事件。

ALTER EVENT event_user DISABLE;

21.6、删除事件

在MySQL 5.1及以后版本中,删除已经创建的事件可以使用DROP EVENT语句来实现。

示例:删除名称为event_user的事件。

DROP EVENT IF EXISTS event_user;

22、mysql触发器

触发器是一种特殊类型的存储过程,它不同于存储过程,主要是通过事件触发而被执行的,即不是主动调用而执行的;而存储过程则需要主动调用其名字执行

触发器:trigger,是指事先为某张表绑定一段代码,当表中的某些内容发生改变(增、删、改)的时候,系统会自动触发代码并执行。

22.1、作用

可在写入数据前,强制检验或者转换数据(保证护数据安全)
触发器发生错误时,前面用户已经执行成功的操作会被撤销,类似事务的回滚

22.2、创建触发器

基本语法

delimiter 自定义结束符号
create trigger 触发器名字 触发时间 触发事件 onfor each row
begin
    -- 触发器内容主体,每行用分号结尾
end
自定义的结束符合

delimiter ;

on 表 for each:触发对象,触发器绑定的实质是表中的所有行,因此当每一行发生指定改变时,触发器就会发生

触发时间

当 SQL 指令发生时,会令行中数据发生变化,而每张表中对应的行有两种状态:数据操作前和操作后

before:表中数据发生改变前的状态
after:表中数据发生改变后的状态

PS:如果 before 触发器失败或者语句本身失败,将不执行 after 触发器(如果有的话)

触发事件

触发器是针对数据发送改变才会被触发,对应的操作只有

INSERT
DELETE
UPDATE

注意事项

在 MySQL 5 中,触发器名必须在每个表中唯一,但不是在每个数据库中唯一,即同一数据库中的两个表可能具有相同名字的触发器
每个表的每个事件每次只允许一个触发器,因此,每个表最多支持 6 个触发器,before/after insert、before/after delete、before/after update
例子

1.首先创建两张表,商品表和订单表

CREATE TABLE `goods`  (
  `id` int(11) NOT NULL COMMENT '商品id',
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品名称',
  `goods_num` int(11) NULL DEFAULT NULL COMMENT '库存',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

CREATE TABLE `orders`  (
  `id` int(11) NOT NULL COMMENT '订单id',
  `goods_id` int(255) NULL DEFAULT NULL COMMENT '商品id',
  `goods_num` int(255) NULL DEFAULT NULL COMMENT '购买商品数量',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;utf8_general_ci ROW_FORMAT = COMPACT;


INSERT INTO `goods` VALUES (1, '手机', 100);
INSERT INTO `goods` VALUES (2, '电脑', 100);

2.如果订单表发生数据插入,对应的商品库存应该减少。因此这里对订单表创建触发器

语法

delimiter ##
-- 创建触发器
create trigger after_insert_order after insert on orders for each row
begin
    -- 更新商品表的库存,这里只指定了更新第一件商品的库存
    update goods set goods_num = goods_num - 1 where id = 1;
end
##

delimiter ;

22.3、查看触发器

1.查看全部触发器
语法:show triggers;
使用黑框框连接的话可以加\G进行格式化。

show triggers \G;

2.查看触发器的创建语句
语法:show create trigger 触发器名字;

22.4、触发触发器

触发不是自动手动触发的,而是在对应的事件发生后才会触发。比如我们创建的触发器,只有在对订单表进行数据操作的时候,触发器才会执行

我们对 orders 表进行数据插入,看看是否触发了触发器

首先我这里黑框框中文乱码,是执行了

set character_set_results=gb2312;

可以看到,在我们对 orders 表进行数据插入的时候,确实 goods 表 id 为 1 的商品的库存发生了改变。但是这是有问题的,即使我们买了 5 个 id 为 1 的商品,对应的 goods 表却只减了 1

如果我们买 5 个 id 为 2 的商品,也只是 goods 表 id 为 1 的商品的发生改变,也是不正确的。

22.5、删除触发器

触发器不能修改,只能删除

语法:drop trigger + 触发器名字

22.6、触发器应用

触发器针对的是数据库中的每一行记录,每行数据在操作前后都会有一个对应的状态,触发器将没有操作之前的状态保存到 old 关键字中,将操作后的状态保存到 new 中

语法:old/new.字段名

需要注意的是,old 和 new 不是所有触发器都有

在这里插入图片描述
根据这个重新创建根据订单数据改变自动修改库存的触发器

delimiter ##
-- 创建触发器
create trigger after_insert_order after insert on orders for each row
begin
    -- new 代表 orders 表中新增的数据
    update goods set goods_num = goods_num - new.goods_num where id = new.goods_id;
end
##
delimiter ;

如果买 5 个 id 为 1 的商品,此时 id 为 1 的商品的库存得到正确的修改。当然,如果买其他种类的商品,最后得到的结果也是正确的,这里就不一一演示了

PS

当然我们还需要考虑一种情况:如果此时商品的库存不够了,该怎么处理?

这里再次创建一个触发器,在新增语句之前执行,也就是进行校验库存。校验通过才执行。

delimiter ##
-- 创建触发器
create trigger before_insert_order before insert on orders for each row
begin
    -- 取出 goods 表中对应 id 的库存
    -- new 代表 orders 表中新增的数据
    select goods_num from goods where id = new.goods_id into @num;
    
    -- 用即将插入的 orders 表中的库存和 goods 表中的库存进行比较
    -- 如果库存不够,中断操作
    if @num < new.goods_num then
        -- 中断操作:暴力解决,主动出错
        insert into xxx values(xxx);
    end if;
end
##
delimiter ;

创建完触发器之后,再次新增,这时候可以发现,当我添加超过库存量的数据的时候,直接就报错了。数据连 orders 表都未能插入,那么肯定就不会执行 insert after 这个触发器了。

同时,如果在触发器中出现错误,那么前面的已经执行的操作也会全部清空

22.7、其他

① mysql触发器不能对同一张表进行修改操作

假如我在 before update 的时候作一条更新语句,随便将里面哪个字段进行更新

delimiter //
create trigger up before update on orders for each row
begin
    update orders set goods_id = 10 where id = new.id;
end;
//
delimiter ;

接下来我用 update 语句对 orders 表进行更新

此时报错了,提示不能进行更新。之后,我又尝试在触发器中进行 insert 和 delete 操作,之后更新的时候还是报同样的错误

因此说明:MySQL 的触发器中不能对本表(本表指的是创建时候声明的表名)进行 insert、update 和 delete 操作,否则会报错。

22.8、优缺点

优点:触发器可以通过数据库中的关联表实现级联更改,即一张表数据的改变会影响其他表的数据
可以保证数据安全,并进行安全校验
缺点:过分依赖触发器,影响数据库的结构,增加数据库的维护成本

23、sql优化

后续会专门整理出来一篇博客,由于篇幅过长,本篇就不整理了。

猜你喜欢

转载自blog.csdn.net/weixin_43888891/article/details/121036037