存储函数
删除存储函数使用以下语句,如果需要修改存储函数,建议直接删除在重建,直接修改比较麻烦。
DROP FUNCTION [IF EXISTS] fn_name;
存储过程与存储函数的区别
关于存储过程与存储函数的区别,只要给出以下几点。
存储过程可以有多个in,out,inout参数,而存储函数只有输入参数类型,而不能带 输入类型
存储过程实现的功能要复杂一些;而存储函数的单一功能性(针对性)更强。
存储过程可以返回多个值;存储函数只能有一个返回值。
存储过程一般独立的来执行;而存储函数可以作为其他sql语句的组成部分来出现。
存储过程可以调用存储函数。但函数不能调用存储过程。
触发器
触发器的概念以及语法
触发器可以简单理解一种特殊的存储过程,之前存储过程的变量定义及流程语句同样适合触发器,唯一不同的是我们只
需要定义触发器,而不用手动调用触发器。从事件触发的角度来说,触发器编写的过程就是触发事件定义的过程。
因为触发器定义好后会随着数据库操作命令的执行而触发,这些具体的操作是INSERT/UPDATE/DELETE。比如
可以在user表中删除记录执行后,通过定义一个触发器把删除的数据自动添加到历史表中保存以便以后可以进行其他操作,创建
触发器的语法如下:
create trigger trigger_name trigger_time
trigger_event on tbl_name
for each row
begin
trigger_simt
end
其中:
trigger_name :触发器名称,用户自行制定;
trigger_time : 触发时机,取值为before 或 after;
trigger_event : 触发事件,取值为 insert update 或 delete ;需要注意的是这些操作命令并不一定严格意义上的命令,
因为像 load data 和 replace 语句也能触发上述事件。load data 语句用于将一个文件装入到一个数据表中,是一系列
的insert 操作,replace 语句类似insert 语句,当表中有 primary key 或 unique 索引时,如果插入的数据和原来
primary key 或 unique 索引一致时,会先删除原来的数据,然后增加一条新数据,也就是说,一条 replace 语句
会等价于一条 insert 语句或者一条delete 语句和上一条insert 语句。
tbl_name : 表示在哪张表上建立触发器:
trigger_stmt: 触发器程体,可以是一句sql语句或者流程语句
for each row : 在mysql中属于固定写法,指明触发器以行作为执行单位,也就是当用户执行删除命令删除3条数据,
与删除动作相关的触发器也会被执行3次。
创建触发器
在日常的数据库开发中,因业务需求,可能需要在插入更新删除时留下数据的日志,这时采用触发器来实现是个非常不错的选择,
下面我们定义一个用户删除事件的触发器,当用户被删除后自动把被删除的数据添加到用户历史表user_history
触发器定于语句如下:
delimiter //
create trigger tr_user_history after delete
on user for each row
begin
insert into user_history(uid,name,pinyin,birth,sex,address,updated)
values(OLD.id,OLD.name,OLD.pinyin,.....);
end
//
delimiter;
上述sql中创建语句的形式与前面的存储过程或者存储函数都很类似,这里有点需要注意的是,使用old/new 关键字可以
获取数据变更前后的记录,其中old用于after时刻,而new用于before时刻的变更。如old.name表示从user表删除的记录的名称。
insert操作一般使用new关键字,update 操作一般使用new和old,而delete操作一般使用old,现在我们从user表删除一条数据,
然后查看user_history 表的数据。
-- 删除user中id为60的用户数据
delete from user where id = 60;
-- 查看历史表
select * from user_history;
显然我们定义的触发器已生效了。
查看触发器
show triggers [from schema_name];
其中,schema_name 即 Schema的名称,在MySQL中Schema 和Database是一样的,Schema指定为数据库名即可。
show triggers \G;
删除触发器
drop trigger 触发器名称
游标
在前面的分析中可知sql的检索操作返回的数据几乎都是以整个集合的形式,也就是说sql善于将多条查询记录集中到一起并
返回,倘若现在需要一行行第处理查询的结果,这对于sql语句来说确实是个难题,好在存在一种称为游标的技术可以解决这个
问题,所谓的游标就是可以将检索出来的数据集合保存在内存中然后依次取出每条数据进行处理,这样就解决了sql语句无法进行
行记录的难题,游标的读取图解如下:
其中有个指针的概念,指针指明了当前行记录的信息,在游标的处理过程中通过移动指针进行逐行读取数据。要明白的是,
游标一般结合存储过程或存储函数或触发器进行使用, 理解了游标的概念后,看看其定义语法
-- 声明游标
declare cursor_name cursor for select 语句;
-- 打开游标
-- open cursor_name;
-- 从游标指针中获取数据
fetch cursor_name into 变量名 [];
-- 关闭游标
close cursor_name;
在使用游标前需要对其进行声明,其中cursor_name表示游标名,cursor for 是固定写法,select 是检索语句,把检索出来的数据存放到游标
中等待处理。下面我们通过一个案例演示并理解游标的使用
delimiter //
-- 创建存储过程
create procedure sp_cursor(out result text)
begin
declare flag bit default 0; -- 定义标识变量用于判断是否退出循环
declare tmp varchar(20) ; -- 定义临时存储变量
declare continue handler for not found set flag =1; --异常处理并设置flag=1
open cur; -- 打开游标
while flag !=1 do
fetch cur into tmp; -- 从游标中取值并存放到tmp中
if flag !=1 then
set result = concat_ws(',',result,tmp); -- 拼接每次获取的结果
end if;
end while;
close cur; -- 关闭游标
end
//
delimiter ;
-- 执行
call sp_user_cursor(@result)
-- 查询结果
select @result;
事务的4个条件 ACID
原子性(Autmic)一致性 隔离性 持久性
在使用事务处理可能涉及到一下命令
-- 声明事务的开始
begin(或 start transaction);
-- 提交整个事务
commit;
-- 回滚到事务初始状态
rollback;
锁以及事务处理分离水平(隔离级别)
悲观锁:假设会发生并发冲突,回避一切可能违反数据完整性的操作。
乐观所:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性,注意乐观锁并不能解读脏读的问题