Mysql—函数和过程

维护mysql 和postgres 的时候会踩到很多坑,这里总结一些,一面后面再遇到。
如果不习惯简书的markdown排版可以点这里,后续的更新可能不一定会及时同步到简书。个人笔记版

执行sql文件

sql脚本可以通过下面两种方式执行:

-- 直接连接执行
mysql --host shanyy.me --user user --database test < test-func.sql
-- source 执行
mysql > source some.sql

mycli执行sql的时候好像老是会报错,不知道为啥,可能是我配置的不对吧,如果遇到同样的情况换mysql自带的就好了。

mysql的函数(function)和过程(procedure)

在维护mysql和procedure中总会遇到需要有些无法通过简单的sql完成的需求,一般会想到sql脚本完成。这时候就会遇到sql函数和过程。函数和过程很像,在官方文档里也是放在一起讲的。mysql官方文档——函数和过程
函数
先说函数,函数相对比较简单,而且限制也多一些。下面是一个官方文档抄过来的例子:

-- 定义函数名和输入参数
mysql> CREATE FUNCTION hello (s CHAR(20))
-- 定义返回参数类型
mysql> RETURNS CHAR(50)
-- 函数代码段
    -> RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)

-- 可以直接在sql语句中执行函数
mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world!  |
+----------------+
1 row in set (0.00 sec)

函数的执行过程和内置函数一模一样,你就把它理解为内置函数就可以了,可以直接在sql中用。

定义一个函数很简单,复杂的函数也就是函数体可能复杂点,下面是一个稍微复杂点的,实际上这个函数屁用没有,就是为了展示几个常见问题:

drop function if exists test;
DELIMITER $$ 
create function test()
returns varchar(100) NONDETERMINISTIC
begin
declare size int;
declare k int;
declare s datetime;
declare m float;
declare x double;
declare c1 int;
set size = 1000;
set k = 0;
while k < size do
select cast(count(*) as char) into c1  from draft;
set k = k + 1;
end while;

repeat
select cast(count(*) as char) into c1 from draft;
set k = k - 1;
until k > 0
end repeat;

if c1 > 1000 then
set c1 = 999;
else 
set c1 = 1001;
end if;
return c1;
end $$ 
DELIMITER ;

为了大家好复制,就不在代码片里直接注释了,下面来逐行分析。

drop function if exists test;
  • [x] 如果已经存在test就丢弃掉,不然第二次创建函数的时候会报重名的错误。
DELIMITER $$ 
  • [x] 定义代码片分隔符,mysql默认见到分号就执行了,这里设置为$$表明见到$$就执行,可以理解问分号。后面可以看到执行完马上又复原了。这个分隔符你想怎么取就怎么取,只要不和其他的冲突就行,比如官方文档里经常会取//
returns varchar(100) NONDETERMINISTIC
  • [x] 这里可以多了一个NONDETERMINISTIC,这个主要是标识你的函数在给定输入的时候输出是否固定的,类似纯函数什么是纯函数,为了简化分析,后面统一称其为纯函数(可能会不严谨)。
    如果是纯函数,则可以写成DETERMINISTIC,如果不是则可以写成NONDETERMINISTIC。如果不写默认为不是纯函数。按官方文档说的是,如果把纯函数标识为非纯函数,则可能会导致性能下降,或者某些性能优化策略失效;反之,把非纯函数标识为纯函数可能导致结果不对。所以如果不确定是不是纯函数还是缺省比较保险。其实判断是不是纯函数的方法很简单,主要看你函数里是否引入了外部变量,比如查表,随机数啊之类的。上面给出官方的例子就是一个纯函数,后面那个复杂一点的就是一个非纯函数。
    按说只要函数中调用了now或者rand就肯定是非纯函数,标记错了计算结果可能会出错。不过mysql文档说明了,可以认为系统会把随机数和时间戳作为默认参数传到函数中去,所以即便标记成NONDETERMINISTIC也不会出错。不过官方文档只是说如果你只调用一次rand才不会出错。原话如下:

A routine that contains the NOW() function (or its synonyms) or RAND() is nondeterministic, but it might still be replication-safe. For NOW(), the binary log includes the timestamp and replicates correctly. RAND() also replicates correctly as long as it is called only a single time during the execution of a routine. (You can consider the routine execution timestamp and random number seed as implicit inputs that are identical on the master and slave.)

begin
...
end
  • [x] begin和end组成了一个代码片,你可以在里面执行函数体的代码,比如这里的函数就在begin和end里。只需要注意别把return之类的句子写到end后面就行了。
  • [ ] 如果函数体代码不是直接返回,就需要加一个begin...end包一下。
  • [x] 后面的就是基本语法了,这里写的三个是比较常用的,两个循环一个条件判断,实际用的过程中只需要照葫芦画瓢就行了,人家非要这么写,你也没办法。

基本常用的函数就照这个这个复杂的例子改改就可以了,不过需要注意的是declare一定要放在最前面,比如上面你把从c1declare放到set的后面看着也没啥问题,实际上就是会报错,我也不知道为啥。

有了这些基础,我们开始尝试着做一些完成一个很常见的需求:如何用mysql快速填充一个测试表。虽然利用python或者nodejs填充表格也很简单,但是效率最高的还是直接使用sql来做。
于是乎,我们尝试写出了下面的代码:

-- 实际上这个代码是无法执行的!!!
drop function if exists initProj;
DELIMITER $$
create function initProj()
begin
declare size int;
declare k int;
drop table tmp_table;
create table tmp_table ( id int(11) not null auto_increment primary key,
                        time datetime,
                        rand_data int(10),
                        rand_str varchar(100));
set size = 1000;
set k = 0;
while size > k do
insert into tmp_table (time, rand_data, rand_str) values (now(), 1000000 * rand(), rand_string(100));
set k = k + 1;
end while ;
end $$
DELIMITER ;

这里之所以错误是因为mysql不允许隐式或显示提交,简单理解就是drop table, create table, truncate table之类的操作都没法执行。有时候你需要清理表格可以考虑利用delete from some_table where id > 0;来做,不过不建议这么做,下面介绍的procedure来做更加优雅。


过程
过程和函数很类似,只不过过程的限制没有函数那么多,带来的副作用就是调用过程需要主动地call procedure而不是直接在sql中调用。

-- 调用procedure的例子
-- \G 参数主要是输出的排版更加友好
call exp_procedure() \G;

过程的定义,函数体都和函数是一样的,这里就不具体介绍了,可以简单理解procedure为一坨代码放到一起实现一个功能。下面是利用procedure和function一起实现的初始化表格的需求。

-- 生成随机字符串
DELIMITER $$
drop function if exists rand_string;
CREATE FUNCTION `rand_string`(n INT)
RETURNS varchar(255)
BEGIN
    DECLARE chars_str varchar(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    DECLARE return_str varchar(255) DEFAULT '';
    DECLARE i INT DEFAULT 0;
    WHILE i < n DO
        SET return_str = concat(return_str,substring(chars_str , FLOOR(1 + RAND()*62 ),1));
        SET i = i +1;
    END WHILE;
    RETURN return_str;
END $$
DELIMITER ;
select rand_string(100);

-- 初始化表格
drop procedure if exists initProj;
DELIMITER $$
create procedure initProj(size int)
begin
declare k int;
drop table tmp_table;
create table tmp_table ( id int(11) not null auto_increment primary key,
                        time datetime,
                        rand_data int(10),
                        rand_str varchar(100));
set k = 0;
while size > k do
    insert into tmp_table (time, rand_data, rand_str) values (now(), 1000000 * rand(), rand_string(100));
    set k = k + 1;
end while ;
end $$
DELIMITER ;

只需要call initProj(100000);即可完成初始化。

其中随机字符串为了图简单没有自己写,网上一搜一大堆,我找了一个拿过来用了,丢掉了一些我不太关心的内容。比如权限之类的,有兴趣可以自己去看,这里吧连接放出来了。随机字符串博客链接

最后初始化十万个数据花费了5分钟的时间, 如果不做随机,速度会更快。

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

推荐阅读更多精彩内容