Mysql触发器trigger
Q:什么是触发器?
A:触发器是与表有关的数据库对象,在满足定义条件时触发,并执行触发器中定义的语句集合。
触发器的特性:
1、有begin end体,begin end;之间的语句可以写的简单或者复杂
2、什么条件会触发:I、D、U
3、什么时候触发:在增删改前或者后
4、触发频率:针对每一行执行
5、触发器定义在表上,附着在表上。
也就是由事件来触发某个操作,事件包括INSERT语句,UPDATE语句和DELETE语句;可以协助应用在数据库端确保数据的完整性。
!!尽量少使用触发器,不建议频繁使用。
假设触发器触发每次执行1s,insert table 500条数据,那么就需要触发500次触发器,光是触发器执行的时间就花费了500s,而insert 500条数据一共是1s,那么这个insert的效率就非常低了。因此我们特别需要注意的一点是触发器的begin end;之间的语句的执行效率一定要高,资源消耗要小。
触发器尽量少的使用,因为不管如何,它还是很消耗资源,如果使用的话要谨慎的使用,确定它是非常高效的:触发器是针对每一行的;对增删改非常频繁的表上切记不要使用触发器,因为它会非常消耗资源。
触发器参数说明:
BEFORE和AFTER参数指定了触发执行的时间,在事件之前或是之后。
> FOR EACH ROW表示任何一条记录上的操作满足触发事件都会触发该触发器,也就是说触发器的触发频率是针对每一行数据触发一次。
> trigger_event详解:
①INSERT型触发器:插入某一行时激活触发器,可能通过INSERT、LOAD DATA、REPLACE 语句触发(LOAD DAT语句用于将一个文件装入到一个数据表中,相当与一系列的INSERT操作);
②UPDATE型触发器:更改某一行时激活触发器,可能通过UPDATE语句触发;
③DELETE型触发器:删除某一行时激活触发器,可能通过DELETE、REPLACE语句触发。
> trigger_order是MySQL5.7之后的一个功能,用于定义多个触发器,使用follows(尾随)或precedes(在…之先)来选择触发器执行的先后顺序。一、创建触发器
1、创建只有一个执行语句的触发器
CREATE TRIGGER 触发器名 BEFORE|AFTER 触发事件 ON 表名 FOR EACH ROW 执行语句;例1:创建了一个名为trig1的触发器,一旦在work表中有插入动作,就会自动往time表里插入当前时间 ```perl
准备2张表,time,work
mysql> create table time(time date); mysql> create table work (id int,name varchar(20),time date);
创建触发器
mysql> CREATE TRIGGER trig1 AFTER INSERT ON work FOR EACH ROW INSERT INTO time VALUES(NOW());
测试
mysql> insert into work(id,name) values(1,’feige’);
查看结果
mysql> select * from time; +——————+ | time | +——————+ | 2021-12-09 | +——————+ 1 row in set (0.00 sec)
再来一次
mysql> insert into work(id,name) values(2,’youngfit’); mysql> select * from time; +——————+ | time | +——————+ | 2021-12-09 | | 2021-12-09 | +——————+ 2 rows in set (0.00 sec)
<a name="Ntzxc"></a>### 2、创建有多个执行语句的触发器```perlCREATE TRIGGER 触发器名 BEFORE|AFTER 触发事件ON 表名 FOR EACH ROWBEGIN执行语句列表END;
例2:定义一个触发器,一旦有满足条件的删除操作,就会执行BEGIN和END中的语句
# work表原本的内容mysql> select * from work;+------+----------+------+| id | name | time |+------+----------+------+| 1 | feige | NULL || 2 | youngfit | NULL |+------+----------+------+2 rows in set (0.00 sec)# 创建1个employee表格mysql> create table employee(id int,name varchar(20),age int);# DELIMITER其实就是告诉mysql解释器,该段命令是否已经结束了,mysql是否可以执行了。默认情况下,delimiter是分号;。在命令行客户端中,如果有一行命令以分号结束,那么回车后,mysql将会执行该命令。# 创建触发器mysql> DELIMITER ||mysql> create trigger trig2 before delete on work for each row begin insert into employee values(1,'feige',18);insert into employee values(2,'youngfit',19);-> END ||mysql> DELIMITER ;mysql> delete from work where id = 1;Query OK, 1 row affected (0.00 sec)mysql> select * from employee;+------+----------+------+| id | name | age |+------+----------+------+| 1 | feige | 18 || 2 | youngfit | 19 |+------+----------+------+2 rows in set (0.00 sec)
3、NEW与OLD详解
MySQL 中定义了 NEW 和 OLD,用来表示触发器的所在表中,触发了触发器的那一行数据,来引用触发器中发生变化的记录内容,具体地:
①在INSERT型触发器中,NEW用来表示将要(BEFORE)或已经(AFTER)插入的新数据;
②在UPDATE型触发器中,OLD用来表示将要或已经被修改的原数据,NEW用来表示将要或已经修改为的新数据;
③在DELETE型触发器中,OLD用来表示将要或已经被删除的原数据;
使用方法:
NEW.columnName (columnName为相应数据表某一列名)
另外,OLD是只读的,而NEW则可以在触发器中使用 SET 赋值,这样不会再次触发触发器,造成循环调用(如每插入一个学生前,都在其学号前加“2013”)。
例3:
mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);mysql> select * from account;

# 创建触发器mysql> delimiter $$mysql> create trigger upd_check before update on account for each row begin-> if new.amount < 0 then-> set new.amount = 0;-> elseif new.amount > 100 then-> set new.amount = 100;-> end if;-> END$$Query OK, 0 rows affected (0.01 sec)mysql> delimiter ;# 案例1:mysql> update account set amount=-10 where acct_num=137;mysql> select * from account;+----------+---------+| acct_num | amount |+----------+---------+| 137 | 0.00 || 141 | 1937.50 || 97 | -100.00 |+----------+---------+3 rows in set (0.00 sec)# 案例2:mysql> update account set amount=200 where acct_num=137;Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from account;+----------+---------+| acct_num | amount |+----------+---------+| 137 | 100.00 || 141 | 1937.50 || 97 | -100.00 |+----------+---------+3 rows in set (0.00 sec)
二、查看触发器
1、SHOW TRIGGERS语句查看触发器信息
mysql> SHOW TRIGGERS\G;

结果,显示所有触发器的基本信息;无法查询指定的触发器。
2、在information_schema.triggers表中查看触发器信息
mysql> SELECT * FROM information_schema.triggers\G

结果,显示所有触发器的详细信息;同时,该方法可以查询制定触发器的详细信息。
mysql> select * from information_schema.triggers where trigger_name='upd_check'\G
4、删除触发器
如果不需要某个触发器时一定要将这个触发器删除,以免造成意外操作,这很关键。
先用上面的命令查询出来,再指定名称删除,如下:
mysql> drop trigger upd_check;Query OK, 0 rows affected (0.00 sec)
Mysql自定义函数
mysql> use test1;mysql> create table tb1(username varchar(20),age int(3));mysql> delimiter //mysql> create function addUser(username varchar(20),age tinyint(3) unsigned)-> returns tinyint(3) unsigned-> begin-> insert tb1(username,age) values(username,age);-> return (select max(age) from tb1);-> end //mysql> delimiter ;mysql> select addUser('chenjs',20);+----------------------+| addUser('chenjs',20) |+----------------------+| 20 |+----------------------+1 row in set (0.00 sec)mysql> select addUser('chenjs',20);+----------------------+| addUser('chenjs',20) |+----------------------+| 20 |+----------------------+1 row in set (0.00 sec)# 查看结果mysql> select * from tb1;+----------+------+| username | age |+----------+------+| chenjs | 20 || chenjs | 20 |+----------+------+2 rows in set (0.00 sec)# 再试一次mysql> select addUser('feige',27);+---------------------+| addUser('feige',27) |+---------------------+| 27 |+---------------------+1 row in set (0.00 sec)mysql> select * from tb1;+----------+------+| username | age |+----------+------+| chenjs | 20 || chenjs | 20 || feige | 27 |+----------+------+3 rows in set (0.00 sec)
Mysql视图
1、基本概念
视图,虚拟表,从一个表或多个表中查询出来的表,作用和真实表一样,包含一系列带有行和列的数据。视图中,用户可以使用SELECT语句查询数据,也可以使用INSERT,UPDATE,DELETE修改记录,视图可以使用户操作方便,并保障数据库系统安全。
视图特点
- 优点
- 简单化,数据所见即所得。
- 安全性,用户只能查询或修改他们所能见到得到的数据。
- 逻辑独立性,可以屏蔽真实表结构变化带来的影响。
- 缺点
- 性能相对较差,简单的查询也会变得稍显复杂。
- 修改不方便,特变是复杂的聚合视图基本无法修改。
大数据分表时可以用到
比如,表的行数超过200万行时,就会变慢,
可以把一张的表的数据拆成4张表来存放.
News表
Newsid, 1,2,3,4
News1,news2,news3,news4表
把一张表的数据分散到4张表里,分散的方法很多,
最常用可以用id取模来计算.
Id%4+1 = [1,2,3,4]
比如 $_GET[‘id’] = 17,
17%4 + 1 = 2, $tableName = ‘news’.’2’
Select * from news2 where id = 17;
还可以用视图, 把4张表形成一张视图
Create view news as select from n1 union select from n2 union……..
2、数据准备
第一张表
1、员工表
mysql> CREATE TABLE t_employee(ID INT PRIMARY KEY AUTO_INCREMENT,NAME CHAR (30) NOT NULL,SEX CHAR (2) NOT NULL ,AGE INT NOT NULL ,DEPARTMENT CHAR (10) NOT NULL ,SALARY INT NOT NULL ,HOME CHAR (30),MARRY CHAR (3) NOT NULL DEFAULT '否' ,HOBBY CHAR (30) )default charset=utf8;
插入数据
INSERT INTO test1.t_employee(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY)VALUES ( NULL , '小红' , '女' ,20, '人事部' , '4000' , '广东' , '否' , '网球' );INSERT INTO test1.t_employee(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY)VALUES ( NULL , '明日' , '女' ,21, '人事部' , '9000' , '北京' , '否' , '网球' );INSERT INTO test1.t_employee(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY)VALUES ( NULL , '天天' , '男' ,22, '研发部' , '8000' , '上海' , '否' , '音乐' );INSERT INTO test1.t_employee(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY)VALUES ( NULL , '大大' , '女' ,23, '研发部' , '9000' , '重庆' , '否' , '无' );INSERT INTO test1.t_employee(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY)VALUES ( NULL , '王下' , '女' ,24, '研发部' , '9000' , '四川' , '是' , '足球' );INSERT INTO test1.t_employee(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY)VALUES ( NULL , '无名' , '男' ,25, '销售部' , '6000' , '福建' , '否' , '游戏' );INSERT INTO test1.t_employee(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY)VALUES ( NULL , '不知道' , '女' ,26, '销售部' , '5000' , '山西' , '否' , '篮球' );
插入结果
第二张表
然后再定义一张员工信息表:
mysql> create TABLE t_employee_detail(ID INT PRIMARY KEY ,POS CHAR (10) NOT NULL ,EXPERENCE CHAR (10) NOT NULL ,CONSTRAINT `FK_ID` FOREIGN KEY (ID) REFERENCES t_employee(ID)) default charset=utf8;
插入数据
INSERT INTO t_employee_detail(ID,POS,EXPERENCE) VALUES (1, '人事管理' , '工作二年' );INSERT INTO t_employee_detail(ID,POS,EXPERENCE) VALUES (2, '人事招聘' , '工作二年' );INSERT INTO t_employee_detail(ID,POS,EXPERENCE) VALUES (3, '初级工程师' , '工作一年' );INSERT INTO t_employee_detail(ID,POS,EXPERENCE) VALUES (4, '中级工程师' , '工作二年' );INSERT INTO t_employee_detail(ID,POS,EXPERENCE) VALUES (5, '高级工程师' , '工作三年' );INSERT INTO t_employee_detail(ID,POS,EXPERENCE) VALUES (6, '销售代表' , '工作二年' );INSERT INTO t_employee_detail(ID,POS,EXPERENCE) VALUES (7, '销售员' , '工作一年' );
插入结果
3、使用案例
- 语法
CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW [db_name.]view_name [(column_list)] AS select_statement [WITH [CASCADED | LOCAL] CHECK OPTION]
通过该语句可以创建视图,若给定了[OR REPLACE],则表示当已具有同名的视图时,将覆盖原视图。select_statement是一个查询语句,这个查询语句可从表或其它的视图中查 询。视图属于数据库,因此需要指定数据库的名称,若未指定时,表示在当前的数据库创建新视图。
表和数据库共享数据库中相同的名称空间,因此,数据库不能包含相同名称的表和视图,并且,视图的列名也不能重复。
在创建视图前应先看看是否有权限:
mysql> SELECT SELECT_priv,create_view_priv from mysql.user WHERE user = 'root';
单表上创建视图
在员工表是创建视图
CREATE VIEW V_VIEW1(ID, NAME , SEX, AGE,DEPARTMENT) AS SELECT ID, NAME , SEX, AGE, DEPARTMENT FROM test1.t_employee;
然后是显示内容:
mysql> select * from V_VIEW1;+----+-----------+-----+-----+------------+| ID | NAME | SEX | AGE | DEPARTMENT |+----+-----------+-----+-----+------------+| 1 | 小红 | 女 | 20 | 人事部 || 2 | 明日 | 女 | 21 | 人事部 || 3 | 天天 | 男 | 22 | 研发部 || 4 | 大大 | 女 | 23 | 研发部 || 5 | 王下 | 女 | 24 | 研发部 || 6 | 无名 | 男 | 25 | 销售部 || 7 | 不知道 | 女 | 26 | 销售部 |+----+-----------+-----+-----+------------+7 rows in set (0.00 sec)
多表上创建视图
mysql> CREATE VIEW V_VIEW2(ID, NAME , SEX, AGE,DEPARTMENT,POS,EXPERENCE) AS SELECT a.ID, a. NAME , a.SEX, a.AGE,a.DEPARTMENT,b.POS,b.EXPERENCE FROM test1.t_employee a,test1.t_employee_detail b WHERE a.ID=b.ID;
查看视图
DESCRIBE指令查看
mysql> describe V_VIEW2;
SHOW TABLE STATUS
mysql> show table status like 'V_VIEW2'\G
SHOW CREATE view命令
mysql> show create view V_VIEW1\Gmysql> show create view V_VIEW2\G
修改视图
CREATE OR REPLACE命令
mysql> CREATE OR REPLACE VIEW V_VIEW1(ID, NAME , SEX) AS SELECT ID, NAME , SEX FROM-> test1.t_employee;
ALTER 命令
mysql> ALTER VIEW V_VIEW1(ID, NAME ) AS SELECT ID, NAME FROM test1.t_employee;
更新视图
在MySQL中,更新视图是指通过视图来插入(INSERT)、更新(UPDATE)和删除(DELETE)表中的数据。因为视图是一个虚拟表,其中没有数据,所以通过视图更新时,都是转换到基本表来更新。
更新视图时,只能更新权限范围内的数据。超出了范围,就不能更新。
更新前:
mysql> select * from V_VIEW2;

更新后:
mysql> UPDATE V_VIEW2 SET POS= '高级工程师' WHERE NAME = '天天';

对应 的真实表上的数据也发生改变 了
mysql> SELECT * FROM test1.t_employee_detail WHERE t_employee_detail.ID=3;

不可更新的视图:
某些视图是可更新的。也就是说,可以在诸如UPDATE、DELETE或INSERT等语句中使用它们,以更新基表的内容。对于可更新的视图,在视图中的行和基表中的行之间必须具有一对一的关系。还有一些特定的其他结构,这类结构会使得视图不可更新。更具体地讲,如果视图包含下述结构中的任何一种,那么它就是不可更新的:
· 聚合函数(SUM(), MIN(), MAX(), COUNT()等)。
· DISTINCT
· GROUP BY
· HAVING
· UNION或UNION ALL
· 位于选择列表中的子查询
· Join
· FROM子句中的不可更新视图
· WHERE子句中的子查询,引用FROM子句中的表。
· 仅引用文字值(在该情况下,没有要更新的基本表)。
· ALGORITHM = TEMPTABLE(使用临时表总会使视图成为不可更新的)。
注意:
视图中虽然可以更新数据,但是有很多的限制。一般情况下,最好将视图作为查询数据的虚拟表,而不要通过视图更新数据。因为,使用视图更新数据时,如果没有全面考虑在视图中更新数据的限制,就可能会造成数据更新失败。
CASCADED和LOCAL能不能决定视图是否能更新?
WITH[CASCADED|LOCAL] CHECK OPTION能不能决定视图是否能更新?这两个参数的基本定义如下:
LOCAL参数表示更新视图时只要满足该视图本身定义的条件即可。
CASCADED参数表示更新视图时需要满足所有相关视图和表的条件。没有指明时,该参数为默认值。
With check option的用法:
(with check option对于没有where条件的视图不起作用的)
mysql> CREATE VIEW V_VIEW3(ID, NAME ,SEX,AGE,DEPARTMENT,SALARY, HOME, MARRY, HOBBY) AS SELECT ID, NAME , SEX,AGE,DEPARTMENT,SALARY,HOME,MARRY,HOBBY FROM test1.t_employee WHERE DEPARTMENT= '人事部' WITH LOCAL CHECK OPTION;
表示只限定插入部门为人事部的人。
然后插入一条:
mysql> INSERT INTO test1.V_VIEW3(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY) VALUES ( NULL , '会会会会' , '女' ,20, '人事部' , '4500' , '广东' , '否' , '网球' );

同时查看真实表中的数据:
mysql> select * from t_employee;

再来插入一条:
mysql> INSERT INTO test1.V_VIEW3(ID, NAME , SEX, AGE,DEPARTMENT, SALARY, HOME, MARRY, HOBBY) VALUES ( NULL , 'qqqqvasvas' , '女' ,20, '研发部' , '4500' , '上海' , '否' , '网球' );ERROR 1369 (HY000): CHECK OPTION failed 'test1.V_VIEW3'
结果显示插入失败
对于with check option用法,总结如下:
通过有with check option选项的视图操作基表(只是面对单表,对连接多表的视图正在寻找答案),有以下结论: 插入后的数据,通过视图能够查询出来就符合WITH CHECK OPTION 否则就不符合;
首先视图只操作它可以查询出来的数据,对于它查询不出的数据,即使基表有,也不可以通过视图来操作。
1.对于update,有with check option,要保证update后,数据要被视图查询出来
2.对于delete,有无with check option都一样
4.对于insert,有with check option,要保证insert后,数据要被视图查询出来
对于没有where 子句的视图,使用with check option是多余的
删除视图
mysql> drop VIEW if exists V_VIEW3;
Mysql存储过程
定义
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。<br />存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,存储和和函数的区别在于函数必须有返回值,而存储过程没有,存储过程的参数可以使用IN、OUT、INOUT类型,而函数的参数只能是IN类型。存储过程再简单点来说,就是为以后的使用而保存的一条或多条MySQL语句的集合。可将其视为批件,虽然它们的作用不仅限于批处理。在我看来, 存储过程就是有业务逻辑和流程的集合, 可以在存储过程中创建表,更新数据, 删除等等。本次博客就来讲一下存储过程
存储过程的特点
1、能完成较复杂的判断和运算<br /> 2、可编程性强,灵活<br /> 3、SQL编程的代码可重复使用<br /> 4、执行的速度相对快一些<br /> 5、减少网络之间的数据传输,节省开销
创建一个简单的存储过程
# 表原来的内容mysql> select * from work;+------+----------+------+| id | name | time |+------+----------+------+| 2 | youngfit | NULL |+------+----------+------+1 row in set (0.00 sec)# 创建存储过程mysql> delimiter //mysql> create procedure testa()-> begin-> select * from work;-> end //mysql> delimiter ;# 调用存储过程mysql> call testa();+------+----------+------+| id | name | time |+------+----------+------+| 2 | youngfit | NULL |+------+----------+------+1 row in set (0.00 sec)Query OK, 0 rows affected (0.00 sec)
存储过程的变量
先通过一个简单的例子来学习变量的声明和赋值
# work表原来的内容mysql> create table work(id int,name varchar(32),time datetime);mysql> insert into work values(2,'youngfit',NULL);mysql> select * from work;+------+----------+------+| id | name | time |+------+----------+------+| 2 | youngfit | NULL |+------+----------+------+1 row in set (0.00 sec)# 创建存储过程,使用变量mysql> delimiter //create procedure test2()begindeclare username varchar(32); --使用decalre声明变量set username='feige'; --使用set设置变量select name into username from work where id=2; --将work表中id=2的名称赋值给username,会将username='feige'覆盖掉select username; --返回变量end //Query OK, 0 rows affected (0.00 sec)mysql> delimiter ;# 调用存储过程;mysql> call test2;+----------+| username |+----------+| youngfit |+----------+1 row in set (0.00 sec)Query OK, 0 rows affected (0.00 sec)
2、概括
(1)、变量的声明使用declare,一句declare只声明一个变量,变量必须先声明后使用;
(2)、变量具有数据类型和长度,与mysql的SQL数据类型保持一致,因此甚至还能制定默认值、字符集和排序规则等;
(3)、变量可以通过set来赋值,也可以通过select into的方式赋值;
(4)、变量需要返回,可以使用select语句,如:select 变量名;
变量的作用域
1、变量作用域说明:<br /> (1)、存储过程中变量是有作用域的,作用范围在begin和end块之间,end结束变量的作用范围即结束。<br /> (2)、需要多个块之间传值,可以使用全局变量,即放在所有代码块之前<br /> (3)、传参变量是全局的,可以在多个块之间起作用<br /> 2、通过一个实例来验证变量的作用域<br /> 需求: 创建一个存储过程,用来统计表users、orders表中行数数量和orders表中的最大金额和最小金额<br />准备第一张表
mysql> create table users(id int primary key auto_increment,name varchar(32),age int,status int,score int,accontid int)default charset=utf8;mysql> insert into users values(1,'张三',19,0,40,10001),(2,'李四',15,1,0,10002);mysql> insert into users values(3,'王五',15,2,0,10001),(5,'王小丽',20,3,0,10003);

准备第二张表
mysql> create table orders(id int primary key auto_increment,order_num bigint,create_date datetime,money int) defaultcharset=utf8;mysql> insert into orders values(1,201902020001,now(),200);Query OK, 1 row affected (0.00 sec)mysql> insert into orders values(2,201902019002,now(),100);Query OK, 1 row affected (0.00 sec)mysql> insert into orders values(3,201902019003,now(),300);Query OK, 1 row affected (0.00 sec)

# 创建存储过程mysql> delimiter //mysql> create procedure test3()beginbegindeclare userscount int default 0; -- 用户表中的数量declare ordercount int default 0; -- 订单表中的数量select count(*) into userscount from users;select count(*) into ordercount from orders;select userscount,ordercount; -- 返回用户表中的数量、订单表中的数量end;begindeclare maxmoney int default 0; -- 最大金额declare minmoney int default 0; -- 最小金额select max(money) into maxmoney from orders;select min(money) into minmoney from orders;select maxmoney,minmoney; -- 返回最大金额、最小金额end;end //mysql> delimiter ;

将userscount,ordercount改为局部变量,再次验证
mysql> delimiter //create procedure test4()beginbegindeclare userscount int default 0; -- 用户表中的数量declare ordercount int default 0; -- 订单表中的数量select count(*) into userscount from users;select count(*) into ordercount from orders;select userscount,ordercount; -- 返回用户表中的数量、订单表中的数量end;begindeclare maxmoney int default 0; -- 最大金额declare minmoney int default 0; -- 最小金额select max(money) into maxmoney from orders;select min(money) into minmoney from orders;select userscount,ordercount,maxmoney,minmoney; -- 返回最金额、最小金额end;end //mysql> delimiter ;mysql> call test4; # 会报错

将userscount,ordercount改为全局变量,再次验证
mysql> delimiter //create procedure test5()begindeclare userscount int default 0; -- 用户表中的数量declare ordercount int default 0; -- 订单表中的数量beginselect count(*) into userscount from users;select count(*) into ordercount from orders;select userscount,ordercount; -- 返回用户表中的数量、订单表中的数量end;begindeclare maxmoney int default 0; -- 最大金额declare minmoney int default 0; -- 最小金额select max(money) into maxmoney from orders;select min(money) into minmoney from orders;select userscount,ordercount,maxmoney,minmoney; -- 返回最金额、最小金额end;end //mysql> delimiter ;

因此,存储过程中变量的作用域,作用范围在begin和end块之间,end结束变量的作用范围即结束
存储过程参数
1、基本语法
create procedure 名称([IN|OUT|INOUT] 参数名 参数数据类型 )begin.........end
存储过程的参数类型有:IN,OUT,INOUT,下面分别介绍这个三种类型:
2、存储过程的传出参数IN
·
说明:
(1)传入参数:类型为in,表示该参数的值必须在调用存储过程时指定,如果不显示指定为in,那么默认就是in类型。<br /> (2)IN类型参数一般只用于传入,在调用过程中一般不作为修改和返回<br /> (3)如果调用存储过程中需要修改和返回值,可以使用OUT类型参数
通过一个实例来演示:
需求:编写存储过程,传入id,根据id返回name
# work表原来的内容mysql> select * from work;+------+----------+------+| id | name | time |+------+----------+------+| 2 | youngfit | NULL |+------+----------+------+1 row in set (0.00 sec)mysql> delimiter //create procedure test2(userid int)begindeclare username varchar(32);set username='feige';select name into username from work where id=userid;select username;end //mysql> delimiter ;mysql> call test2(2);+----------+| username |+----------+| youngfit |+----------+1 row in set (0.00 sec)Query OK, 0 rows affected (0.00 sec)
另一张表做测试:
# users表中原来的内容们,这里修改了name下面的字段;mysql> select * from users;+----+----------+------+--------+-------+----------+| id | name | age | status | score | accontid |+----+----------+------+--------+-------+----------+| 1 | feige | 19 | 0 | 40 | 10001 || 2 | youngfit | 15 | 1 | 0 | 10002 || 3 | hadeng | 15 | 2 | 0 | 10001 || 5 | james | 20 | 3 | 0 | 10003 |+----+----------+------+--------+-------+----------+4 rows in set (0.00 sec)mysql> delimiter //create procedure test6(in userId int) --这里就用了inbegindeclare username varchar(20);declare ordercount int default 0;select name into username from users where id=userId;select username;end //mysql> delimiter ;mysql> call test6(3);+----------+| username |+----------+| hadeng |+----------+1 row in set (0.01 sec)
3、存储过程的传出参数out
需求:调用存储过程时,传入userId返回该用户的name
create procedure test5(in userId int,out username varchar(32))beginselect name into username from users where id=userId;end;
调用以及运行结果如图:
概括:
1、传出参数:在调用存储过程中,可以改变其值,并可返回;
2、out是传出参数,不能用于传入参数值;
3、调用存储过程时,out参数也需要指定,但必须是变量,不能是常量;
4、如果既需要传入,同时又需要传出,则可以使用INOUT类型参数
4、存储过程的可变参数INOUT
需求:调用存储过程时,传入userId和userName,即使传入,也是传出参数。
mysql> delimiter //create procedure test6(inout userId int,inout username varchar(32))beginset userId=2;set username='';select id,name into userId,username from users where id=userId;end //mysql> delimiter ;# 调用以及结果显示:mysql> set @uname='';Query OK, 0 rows affected (0.00 sec)mysql> set @userid=0;Query OK, 0 rows affected (0.00 sec)mysql> call test6(@userid,@uname);Query OK, 1 row affected (0.00 sec)mysql> select @userid,@uname as username;+---------+----------+| @userid | username |+---------+----------+| 2 | youngfit |+---------+----------+1 row in set (0.00 sec)
概括:
1、可变变量INOUT:调用时可传入值,在调用过程中,可修改其值,同时也可返回值;
2、INOUT参数集合了IN和OUT类型的参数功能;
3、INOUT调用时传入的是变量,而不是常量;
存储过程条件语句
1、基本结构
(1)条件语句基本结构:
if() then...else...end if;
(2)、多条件判断语句:
if() then...elseif() then...else ...end if;
2、实例
实例1:编写存储过程,如果用户userId是偶数则返回username,否则返回userId
mysql> delimiter //create procedure test7(in userId int)begindeclare username varchar(32) default '';if(userId%2=0)thenselect name into username from users where id=userId;select username;elseselect userId;end if;end //mysql> delimiter ;
调用以及运行结果:
# 传入奇数mysql> call test7(5);+--------+| userId |+--------+| 5 |+--------+1 row in set (0.00 sec)Query OK, 0 rows affected (0.00 sec)
# 传入偶数mysql> call test7(2);+----------+| username |+----------+| youngfit |+----------+1 row in set (0.00 sec)Query OK, 0 rows affected (0.00 sec)
2、存储过程的多条件语句应用示例
需求:根据用户传入的uid参数判断
(1)如果用户状态status为1,则给用户score加10分
(2)如果用户状态status为2,则给用户score加20分
(3)其他情况加30分
先看users表中原来的数据:
mysql> delimiter //create procedure test8(in userid int)begindeclare my_status int default 0;select status into my_status from users where id=userid;if(my_status=1)thenupdate users set score=score+10 where id=userid;elseif(my_status=2)thenupdate users set score=score+20 where id=userid;elseupdate users set score=score+30 where id=userid;end if;end //mysql> delimiter ;mysql> call test8(5);
存储过程循环语句
1、while语句
(1)while语句的基本结构
while(表达式) do......end while;
(2)示例
需求:使用循环语句,向表test1(id)中插入10条连续的记录
# 先创建1个test1表格mysql> create table test1(id int)mysql> delimiter //create procedure test9()begindeclare i int default 0;while(i<10) dobeginselect i;set i=i+1;insert into test1(id) values(i);end;end while;end //mysql> delimiter ;mysql> call test9;

2、repeat语句
(1)repeat语句基本的结构:
repeat...until...end repeat;
(2)示例
需求:给test1表中的id字段插入数据,从1到10
mysql> delimiter //create procedure test13()begindeclare i int default 0;repeatbeginselect i;set i=i+1;insert into test1(id) values(i);end;until i>=10 -- 如果i>=10,则跳出循环end repeat;end //mysql> delimiter;mysql> call test1;

概括:
until判断返回逻辑真或者假,表达式可以是任意返回真或者假的表达式,只有当until语句为真是,循环结束。
存储过程游标的使用
1、什么是游标
游标是保存查询结果的临时区域
2、示例
需求:编写存储过程,使用游标,把users表中 id为偶数的记录逐一更新用户名
mysql> delimiter //create procedure test11()begindeclare stopflag int default 0;declare username VARCHAR(32);-- 创建一个游标变量,declare 变量名 cursor ...declare username_cur cursor for select name from users where id%2=0;-- 游标是保存查询结果的临时区域-- 游标变量username_cur保存了查询的临时结果,实际上就是结果集-- 当游标变量中保存的结果都查询一遍(遍历),到达结尾,将变量stopflag设置为1,用于循环中判断是否结束declare continue handler for not found set stopflag=1;open username_cur; -- 打卡游标fetch username_cur into username; -- 游标向前走一步,取出一条记录放到变量username中while(stopflag=0) do -- 如果游标还没有结尾,就继续begin-- 在用户名前门拼接 '_cur' 字符串update users set name=CONCAT(username,'_cur') where name=username;fetch username_cur into username;end;end while; -- 结束循环close username_cur; -- 关闭游标end;mysql> delimiter ;

