一、存储
PS:存储过程是数据库存储的一个重要的功能,但是 MySQL 在 5.0 以前并不支持存储过程,这使得 MySQL 在应用上大打折扣。好在 MySQL 5.0 终于开始已经支持存储过程,这样即可以大大提高数据库的处理速度,同时也可以提高数据库编程的灵活性。
1.存储概述
- 存储过程是一组为了完成特定功能的SQL语句集合。使用存储过程的目的就是将常用或复杂的工作预先用SQL语句写好并用一个指定名称存储起来,这个过程编译和优化后存储在数据库服务器中,因此称为存储过程。当以后需要数据库提供与定义好的存储过程的功能相同的服务时,只需要调用“CALL 存储过程名称”即可使用相应的功能!
- 操作数据库的 SQL 语句在执行的时候需要先编译,然后执行。存储过程则采用另一种方式来执行 SQL 语句。
- 一个存储过程是一个可编程的函数,它在数据库中创建并保存,一般由SQL语句和一些特殊的控制结构组成!当希望在不同的应用程序或平台上执行相应的特定功能时,存储过程就变得尤为合适!
2.存储优点
- 封装性 存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的 SQL 语句,并且数据库专业人员可以随时对存储过程进行修改,而不会影响到调用它的应用程序源代码。
- 可增强 SQL 语句的功能和灵活性 存储过程可以用流程控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。
- 可减少网络流量 由于存储过程是在服务器端运行的,且执行速度快,因此当客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而可降低网络负载。
- 高性能 存储过程执行一次后,产生的二进制代码就驻留在缓冲区,在以后的调用中,只需要从缓冲区中执行二进制代码即可,从而提高了系统的效率和性能。
- 提高数据库的安全性和数据的完整性 使用存储过程可以完成所有数据库操作,并且可以通过编程的方式控制数据库信息访问的权限。
3.存储过程创建语法
mysql> delimiter ?? #更改其默认的分隔符为“??”,也可以是其他任意符号,只要不是默认的“;”就行
mysql> create procedure name() #定义存储过程name
-> begin #存储过程开始
-> …… #存放的可以是一些sql语句的集合,当然,它同样有一些判断、循环等语句!!!
-> end ?? #存储过程结束
mysql> delimiter ; #更改为默认的分割符,注意中间必须有空格!!!
mysql> call name(); #调用刚才定义的name存储过程
1. 自定义存储过程
1.1先插入表
mysql> create database cunchu;
Query OK, 1 row affected (0.00 sec)
mysql> use cunchu;
Database changed
mysql> create table roster
-> (
-> id int,
-> name varchar(20),
-> sex varchar(20),
-> scores float
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> insert into roster
-> values
-> (1,'张三','男',90.5),
-> (2,'李四','男',85.5),
-> (3,'王五','女',78.5),
-> (4,'刘三','女',66.5),
-> (5,'赵四','男',50.5);
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from roster;
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 1 | 张三 | 男 | 90.5 |
| 2 | 李四 | 男 | 85.5 |
| 3 | 王五 | 女 | 78.5 |
| 4 | 刘三 | 女 | 66.5 |
| 5 | 赵四 | 男 | 50.5 |
+------+--------+------+--------+
5 rows in set (0.00 sec)
1.2 编写存储过程
以下例子为查看花名册中的所有数据!!!
mysql> delimiter !!
mysql> create procedure test1()
-> begin
-> select * from roster; #查询roster表内容
-> end !!
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> call test1();
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 1 | 张三 | 男 | 90.5 |
| 2 | 李四 | 男 | 85.5 |
| 3 | 王五 | 女 | 78.5 |
| 4 | 刘三 | 女 | 66.5 |
| 5 | 赵四 | 男 | 50.5 |
+------+--------+------+--------+
5 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
2. while语句的存储过程
以下例子为从1+2+3+4……+100 等于多少!!!
mysql> delimiter @@
mysql> create procedure test2()
-> begin
-> declare i int; #定义i为变量名称
-> declare summary int; #定义summary为变量名称
-> set i=1; #设置变量的初始值为1
-> set summary=0; #设置变量的初始值为0
-> while i<=100 #当i小于或等于100时,执行以下操作
-> do
-> set summary=summary+i;
-> set i=i+1;
-> end while ; #循环结束
-> select summary; #查询summary的值
-> end @@
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> call test2();
+---------+
| summary |
+---------+
| 5050 |
+---------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
3. if判断语句的存储过程
以下例子为查找花名册中为男性的学生!!!
mysql> delimiter @@
mysql> create procedure test3(in t char)
-> begin
-> if t="男" then #if语句定义t为男
-> select * from roster where sex="男"; #查询roster中sex为男
-> else
-> select * from roster where sex="女"; #查询roster中sex为女
-> end if; #if语句结束
-> end @@
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> call test3("男");
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 1 | 张三 | 男 | 90.5 |
| 2 | 李四 | 男 | 85.5 |
| 5 | 赵四 | 男 | 50.5 |
+------+--------+------+--------+
3 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call test3("女");
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 3 | 王五 | 女 | 78.5 |
| 4 | 刘三 | 女 | 66.5 |
+------+--------+------+--------+
2 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
4. case语句的存储过程
以下例子为查找花名册中的成绩!!!
mysql> delimiter @@
mysql> create procedure test4(in t int)
-> begin
-> case t
-> when 1 then
-> select * from roster where scores>90; #查询roster表中scores为大于90
-> when 2 then
-> select * from roster where scores>80 and scores<=90; #查询roster表中scores为大于80和小于90
-> when 3 then
-> select * from roster where scores>60 and scores<=80; #查询roster表中scores为大于90和小于80
-> when 4 then
-> select * from roster where scores<60; #查询roster表中scores为小于60
-> else
-> select * from roster; #查询结果无则执行这条命令
-> end case;
-> end @@
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> call test4(1);
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 1 | 张三 | 男 | 90.5 |
+------+--------+------+--------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call test4(2);
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 2 | 李四 | 男 | 85.5 |
+------+--------+------+--------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call test4(3);
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 3 | 王五 | 女 | 78.5 |
| 4 | 刘三 | 女 | 66.5 |
+------+--------+------+--------+
2 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call test4(4);
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 5 | 赵四 | 男 | 50.5 |
+------+--------+------+--------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call test4(5);
+------+--------+------+--------+
| id | name | sex | scores |
+------+--------+------+--------+
| 1 | 张三 | 男 | 90.5 |
| 2 | 李四 | 男 | 85.5 |
| 3 | 王五 | 女 | 78.5 |
| 4 | 刘三 | 女 | 66.5 |
| 5 | 赵四 | 男 | 50.5 |
+------+--------+------+--------+
5 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
5. 修改存储过程
语法:
mysql> help alter procedure
Name: 'ALTER PROCEDURE'
Description:
Syntax:
ALTER PROCEDURE proc_name [characteristic ...]
characteristic: {
COMMENT 'string'
| LANGUAGE SQL
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
# 包含SQL 没有sql 读取SQL数据 修改SQL数据
| SQL SECURITY { DEFINER | INVOKER }
# 定义者 | 调用程序
}
修改存储过程roster的定义,将读写权限改为 MODIFIES SQL DATA,并指明调用者可以执行
mysql> alter procedure test1 MODIFIES SQL DATA SQL SECURITY INVOKER;
Query OK, 0 rows affected (0.00 sec)
mysql> show create procedure test1\G;
*************************** 1. row ***************************
Procedure: test1
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
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `test1`()
MODIFIES SQL DATA
SQL SECURITY INVOKER
begin
select * from roster;
end
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)
6. 删除存储
- 过程名 指定要删除的存储过程的名称。
- IF EXISTS 指定这个关键字,用于防止因删除不存在的存储过程而引发的错误。 注意:存储过程名称后面没有参数列表,也没有括号,在删除之前,必须确认该存储过程没有任何依赖关系,否则会导致其他与之关联的存储过程无法运行。
mysql> drop procedure if exists test1;
Query OK, 0 rows affected (0.00 sec)
mysql> call test1();
ERROR 1305 (42000): PROCEDURE cunchu.test1 does not exist
二、触发器
1.概述
MySQL 数据库中触发器是一个特殊的存储过程,不同的是执行存储过程要使用 CALL 语句来调用,而触发器的执行不需要使用 CALL 语句来调用,也不需要手工启动,只要一个预定义的事件发生就会被 MySQL自动调用。
2.触发器的优点
- 触发程序的执行是自动的,当对触发程序相关表的数据做出相应的修改后立即执行。
- 触发程序可以通过数据库中相关的表层叠修改另外的表。
- 触发程序可以实施比 FOREIGN KEY 约束、CHECK 约束更为复杂的检查和操作。
3.触发器的作用
触发器与表关系密切,主要用于保护表中的数据。特别是当有多个表具有一定的相互联系的时候,触发器能够让不同的表保持数据的一致性。只有执行 INSERT、UPDATE 和 DELETE 操作时才能激活触发器。
4.MySQL 所支持的触发器有
INSERT 触发器
- 在 INSERT 触发器代码内,可引用一个名为 NEW(不区分大小写)的虚拟表来访问被插入的行。
- 在 BEFORE INSERT 触发器中,NEW 中的值也可以被更新,即允许更改被插入的值(只要具有对应的操作权限)。
- 对于 AUTO_INCREMENT 列,NEW 在 INSERT 执行之前包含的值是 0,在 INSERT 执行之后将包含新的自动生成值。
UPDATE 触发器
- 在 UPDATE 触发器代码内,可引用一个名为 NEW(不区分大小写)的虚拟表来访问更新的值。
- 在 UPDATE 触发器代码内,可引用一个名为 OLD(不区分大小写)的虚拟表来访问 UPDATE 语句执行前的值。
- 在 BEFORE UPDATE 触发器中,NEW 中的值可能也被更新,即允许更改将要用于 UPDATE 语句中的值(只要具有对应的操作权限)。 OLD 中的值全部是只读的,不能被更新。 注意:当触发器设计对触发表自身的更新操作时,只能使用 BEFORE 类型的触发器,AFTER 类型的触发器将不被允许。
DELETE 触发器
- 在 DELETE 触发器代码内,可以引用一个名为 OLD(不区分大小写)的虚拟表来访问被删除的行。 OLD 中的值全部是只l读的,不能被更新。
5.触发器使用的过程中,MySQL 会按照以下方式来处理错误
- 若对于事务性表,如果触发程序失败,以及由此导致的整个语句失败,那么该语句所执行的所有更改将回滚;对于非事务性表,则不能执行此类回滚,即使语句失败,失败之前所做的任何更改依然有效。
- 若 BEFORE 触发程序失败,则 MySQL 将不执行相应行上的操作。
- 若在 BEFORE 或 AFTER 触发程序的执行过程中出现错误,则将导致调用触发程序的整个语句失败。
- 仅当 BEFORE 触发程序和行操作均已被成功执行,MySQL 才会执行AFTER触发程序。
创建触发器语法:
查看帮助
mysql> help create trigger
Name: 'CREATE TRIGGER'
Description:
Syntax:
CREATE
[DEFINER = user]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
[trigger_order]
trigger_body
trigger_time: { BEFORE | AFTER }
# 之前 | 之后
trigger_event: { INSERT | UPDATE | DELETE }
trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
语法:
mysql> delimiter $$ #更改其默认的分隔符为“$$”,也可以是其他任意符号,只要不是默认的“;”就行
mysql> create trigger test #为触发器定义名字为test
-> after insert #选择after
-> on 表名 for each row
-> begin #开启
-> …… #自定义sql语句
-> end $$ #结束
mysql> delimiter ; #更改为默认的分割符,注意中间必须有空格!!!
1.创建表并插入数据
mysql> create table goods
-> (
-> g_id int,
-> g_name varchar(30),
-> quantity int
-> );
Query OK, 0 rows affected (0.08 sec)
mysql> create table orders
-> (
-> o_id int ,
-> g_id int,
-> counts int,
-> price int
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into goods
-> values
-> (1,'apple','100'),
-> (2,'banana','100'),
-> (3,'pineapple','100');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 100 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
2.insert语句
以下例子为商品卖出后库存表自动减去相对应的商品!!!
mysql> delimiter $$
mysql> create trigger test5
-> after insert
-> on orders for each row
-> begin
-> update goods set quantity=quantity-new.counts where g_id=new.g_id;
-> end $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
#查看原先数据
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 100 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
#插入数据
mysql> insert into orders values(1,2,10,55);
Query OK, 1 row affected (0.00 sec)
#查看插入之后的对应数据是否对应
mysql> select * from orders;
+------+------+--------+-------+
| o_id | g_id | counts | price |
+------+------+--------+-------+
| 1 | 2 | 10 | 55 |
+------+------+--------+-------+
1 row in set (0.00 sec)
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 90 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
3.update语句
mysql> delimiter $$
mysql> create trigger test6
-> after update
-> on orders for each row
-> begin
-> update goods set quantity=quantity-(new.counts-old.counts) where g_id=new.g_id;
-> end $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
3.2 增加
以下为客户吧订单加大,库存表自动变化!!!
#查看俩个表的数据
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 90 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
mysql> select * from orders;
+------+------+--------+-------+
| o_id | g_id | counts | price |
+------+------+--------+-------+
| 1 | 2 | 10 | 55 |
+------+------+--------+-------+
1 row in set (0.00 sec)
#更新数据
mysql> update orders set counts=20 where o_id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#查看更新之后的俩表的数据
mysql> select * from orders;
+------+------+--------+-------+
| o_id | g_id | counts | price |
+------+------+--------+-------+
| 1 | 2 | 20 | 55 |
+------+------+--------+-------+
1 row in set (0.00 sec)
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 80 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
3.3 减少
以下为客户退货之后库存自动变化为实时数据!!!
#更改数据
mysql> update orders set counts =5 where o_id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#查看更改之后的数据
mysql> select * from orders;
+------+------+--------+-------+
| o_id | g_id | counts | price |
+------+------+--------+-------+
| 1 | 2 | 5 | 55 |
+------+------+--------+-------+
1 row in set (0.00 sec)
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 95 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
4.delete语句
以下为客户退货之后表内自动转换相对应的数据!!!
mysql> delimiter $$
mysql> create trigger test7
-> after delete
-> on orders for each row
-> begin
-> update goods set quantity=quantity+old.counts where g_id=old.g_id;
-> end $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
#查看原先数据
mysql> select * from orders;
+------+------+--------+-------+
| o_id | g_id | counts | price |
+------+------+--------+-------+
| 1 | 2 | 5 | 55 |
+------+------+--------+-------+
1 row in set (0.00 sec)
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 95 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
#删除出货表的数据
mysql> delete from orders where o_id =1;
Query OK, 1 row affected (0.01 sec)
#查看删除之后的对应数据
mysql> select * from orders;
Empty set (0.00 sec)
mysql> select * from goods;
+------+-----------+----------+
| g_id | g_name | quantity |
+------+-----------+----------+
| 1 | apple | 100 |
| 2 | banana | 100 |
| 3 | pineapple | 100 |
+------+-----------+----------+
3 rows in set (0.00 sec)
5.删除触发器
#查看触发器
mysql> show create trigger test7\G
*************************** 1. row ***************************
Trigger: test7
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
SQL Original Statement: CREATE DEFINER=`root`@`localhost` trigger test7
after delete
on orders for each row
begin
update goods set quantity=quantity+old.counts where g_id=old.g_id;
end
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
Created: 2020-12-27 20:40:01.82
1 row in set (0.00 sec)
#删除触发器
mysql> drop trigger test7;
Query OK, 0 rows affected (0.00 sec)
mysql> show create trigger test7\G
ERROR 1360 (HY000): Trigger does not exist