MySQL存储过程和存储函数

一、存储过程

MySQL存储过程
存储过程和存储函数参考文章

SQL语句需要先编译然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。

存储过程是可编程的函数,在数据库中创建并保存,可以由SQL语句和控制结构组成。当想要在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟,它允许控制数据的访问方式。

存储过程的优点:
(1)存储过程是预编译过的,执行效率高;
(2)存储过程的代码是直接防御数据库中,通过存储过程名直接调用,减少网络通讯;
(3)安全性高,执行存储过程需要有一定的权限用户;
(4)存储过程可以重复使用,可减少数据库开发人员的工作量;

存储过程的缺点==>(可移植性差)
(1)存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。(可移植性差)
(2)存储过程的性能调校与撰写,受限于各种数据库系统。

存储过程的创建和调用

存储过程就是具有名字的一段代码,用来完成一个特定的功能。创建的存储过程保存在数据库的数据字典中。

// 声明存储过程
CREATE
    [DEFINER = { user | CURRENT_USER }]
 PROCEDURE sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body

// 存储过程的参数
proc_parameter:
    [ IN | OUT | INOUT ] param_name type

// 指定存储过程的特性
characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }
 
// SQL代码的内容,可以用BEGIN...END来表示SQL代码的开始与结束
routine_body:
  Valid SQL routine statement
 
[begin_label:] BEGIN
  [statement_list]
    ……
END [end_label]

实例

创建数据库,备份数据表用于示例操作:

mysql> create database db1;
mysql> use db1;    
mysql> create table PLAYERS as select * from TENNIS.PLAYERS;
mysql> create table MATCHES  as select * from TENNIS.MATCHES;

下面是存储过程的例子,删除给定球员参加的所有比赛:

mysql> delimiter $$  // 将语句的结束符号从分号;临时改为两个$$(可以是自定义)
mysql> CREATE PROCEDURE delete_matches(IN p_playerno INTEGER)
    -> BEGIN
    ->   DELETE FROM MATCHES
    ->    WHERE playerno = p_playerno;
    -> END$$
Query OK, 0 rows affected (0.01 sec)
 
mysql> delimiter;  // 将语句的结束符号恢复为分号

存储过程的调用

call sp_name[(传参)];
mysql> select * from MATCHES;
+---------+--------+----------+-----+------+
| MATCHNO | TEAMNO | PLAYERNO | WON | LOST |
+---------+--------+----------+-----+------+
|       1 |      1 |        6 |   3 |    1 |
|       7 |      1 |       57 |   3 |    0 |
|       8 |      1 |        8 |   0 |    3 |
|       9 |      2 |       27 |   3 |    2 |
|      11 |      2 |      112 |   2 |    3 |
+---------+--------+----------+-----+------+
5 rows in set (0.00 sec)
 
mysql> call delete_matches(57);
Query OK, 1 row affected (0.03 sec)
 
mysql> select * from MATCHES;
+---------+--------+----------+-----+------+
| MATCHNO | TEAMNO | PLAYERNO | WON | LOST |
+---------+--------+----------+-----+------+
|       1 |      1 |        6 |   3 |    1 |
|       8 |      1 |        8 |   0 |    3 |
|       9 |      2 |       27 |   3 |    2 |
|      11 |      2 |      112 |   2 |    3 |
+---------+--------+----------+-----+------+
4 rows in set (0.00 sec)

二、存储函数

基本语法:

create function func_name([func_parameter]) returns type
[characteristic...] routine_body

create function:创建存储函数的关键字;
func_name:存储函数的名称;
func_parameter:存储过程的参数列表;格式:para_name para_type ...
returns type :表示函数返回数据的类型;
characteristic指定存储函数的特性,取值与创建存储过程时相同

例子:

#定义函数
delimiter $$
create function getGameName(gameid int) returns VARCHAR(45)
DETERMINISTIC
begin
   declare name VARCHAR(45);
   set name=(select gamename from cy_game where id=gameid);
   return (name);
end$$
delimiter;

#使用函数(可以直接在select中使用)
select getGameName(4);
select id,getGameName(4) from cy_game where id=4;

前面有一句,“可以处于任何使用SQL语句的目的来使用存储过程”。这里想说的是,有些时候有些地方使用函数或许会更方便些。例如,存在这样一个表:Temperature(Year, Month, Day, T02, T08, T14, T20),其中Year,Month,Day是时间字段,T02, T08, T14, T20是指2时、8时、14时、20时四个时刻对应的温度值,这些温度值可为空。现在,要求统计2008年5月份的平均温度。

或许大家会这样写:

SELECT (AVG(T02)+AVG(T08)+AVG(T14)+AVG(T20))/4 FROM Temperature WHERE Year=2008 AND Month=5

如果不考虑空值的话,这样完全正确,但是考虑空值的话,如果根本没有统计02时的温度,那么AVG(T02)将为NULL,然后进行所有运算的结果都将为NULL。这显然是不正确的。这里可以创建一个自定义函数,然后使用一个SELECT语句即可查询:

SELECT AVG(user.Average(T02,T08,T14,T20)) FROM Temperature WHERE Year=2008 AND Month=5

对应的存储方法实现如下:

CREATE FUNCTION user.Average

(

    @T02 float,

    @T08 float,

    @T14 float,

    @T20 float

)
RETURNS float AS 
BEGIN

DECLARE @sum float
DECLARE @num int
DECLARE @Ret float

SET @sum=0
SET @num=0

IF @T02 IS NOT NULL
BEGIN
    SET @sum = @sum + @T02
    SET @num = @num + 1
END

IF @T08 IS NOT NULL
BEGIN
    SET @sum = @sum + @T08
    SET @num = @num + 1
END

IF @T14 IS NOT NULL
BEGIN
    SET @sum = @sum + @T14
    SET @num = @num + 1
END

IF @T20 IS NOT NULL
BEGIN
    SET @sum = @sum + @T20
    SET @num = @num + 1
END

IF @num>0   SET @Ret = @sum / @num

Return @Ret

END

GO

三、存储过程、存储函数适用的场景

什么时候用存储过程,什么时候用函数

核心提示:本质上没区别。只是函数有限制只能返回一个标量,而存储过程可以返回多个。并且函数是可以嵌入在SQL中使用的,可以在SELECT等SQL语句中调用,而存储过程不行。执行的本质都一样。

存储过程和函数具体的区别:
存储过程:可以使得对的管理、以及显示关于及其用户信息的工作容易得多。存储过程是 SQL 语句和可选控制流语句的预编译集合,以一个名称存储并作为一个单元处理。存储过程存储在数据库内,可由应用程序通过一个调用执行,而且允许用户声明变量、有条件执行以及其它强大的编程功能。存储过程可包含程序流、逻辑以及对数据库的查询。它们可以接受参数、输出参数、返回单个或多个结果集以及返回值。
可以出于任何使用 SQL 语句的目的来使用存储过程,它具有以下优点:

(1)功能强大,限制少。
(2)可以在单个存储过程中执行一系列 SQL 语句。
(3)可以从自己的存储过程内引用其它存储过程,这可以简化一系列复杂语句。
(4)存储过程在创建时即在上进行编译,所以执行起来比单个 SQL 语句快。
(5)可以有多个返回值,即多个输出参数,并且可以使用SELECT返回结果集。

函数:是由一个或多个 SQL 语句组成的子程序,可用于封装代码以便重新使用。自定义函数诸多限制,有许多语句不能使用,许多功能不能实现。函数可以直接引用返回值,用表变量返回记录集。但是,用户定义函数不能用于执行一组修改全局数据库状态的操作。

因此存储过程是不直接return返回值的,通过设置输入的参数类型,IN | OUT | INOUT来获取处理过的结果的,使用时只能单独调用;而函数却能出现在能放置表达式的任何位置(可以用于select语句中)。 存储过程一般是作为一个独立的部分来执行,而函数可以作为查询语句的一个部分来调用,由于函数可以返回一个表对象,因此它可以在查询语句中位于FROM关键字的后面。

存储过程与函数的区别?

本质上没区别。函数只能通过return语句返回单个值或者表对象。而存储过程不允许执行return,但是通过out参数返回多个值。 函数是可以嵌入在sql中使用的,可以在select中调用,而存储过程不行。函数限制比较多,如不能用临时表,只能用表变量等,而存储过程的限制相对就比较少。

  1. 一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强。

  2. 当对数据库进行复杂操作时(如对多个表进行Update、Insert、Query、Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。存储过程可以从自己的存储过程内引用其它存储过程,这可以简化一系列复杂语句.

  1. 存储过程一般是作为一个独立的部分来执行,而函数可以作为查询语句的一个部分来调用,由于函数可以返回一个表对象,因此它可以在查询语句中位于FROM关键字的后面。
  1. 存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
  1. 存储过程可以接受参数、输出参数、返回单个或多个结果集(多个输出参数的意思,不是一堆数据,不要和表数据弄混)以及返回值,可以向程序返回错误原因。但函数只能返回一个特定类型的值或者表对象。
  1. 存储过程中的CRUD的操作会影响数据库状态,但函数却不能。

7. 函数只能是in类型,存储过程可以使用In\out\inout类型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352