最近工作中需要同时插入两张表,老大非要让使用存储过程,因为我们使用的是PostgreSQL,所以就去学习了一下这个PostgreSQL 的存储过程。
存储过程,在PostgreSQL 这个数据库中,被称为PostgreSQL函数,所以你可以把他看成就是我们平常使用的函数,只是定义和调用的时候,语法的不同。
基本语法:
GREATE [OR REPLACE] FUNCTION function_name (arguments) RETURNS return_datatype AS $variable_name$ DECLARE Declaration; [...] BEGIN <function_body> [...] RETURN{variable_name|value} END; LANGUAGE plpgsql;
参数说明:
[OR REPLACE]:是可选的,叫上表示允许修改和替换现有函数。
比如你创建了一个postgreSQL 函数“insert_data”里面的有一些插入数据的功能,然后在不久后你又创建了一个“insert_data” 的PostgreSQL 函数,则后面这个会替换掉前面那个。
[RETURN]:指定要从函数返回的数据类型,可以是基础,复合或域类型,也可以是引用表列的类型。
块名解释:
DECLARE:在此声明在下方可能使用到的变量,需要指定该变量的类型
BEGIN...END : 被称为函数的主体,就是具体需要执行的代码,一般是数据库的操作语言,如DQL(查询语句),DML(增删改操纵语句)
LANGUAGE : CREATE FUNCTION命令的语法要求函数体写成一个字符串文本。通常来说,该文本字符串常量使用两个美元符($$)包裹,然后在最后面指明该字符串文本的语言类型,PostgreSQL 函数中一般使用plpgsql
具体看一个简单的查看students 表中的所有学生的总年龄的例子,注: “--” 在postgreSQL 中代表的是单行注释:
-- SQL代码块
CREATE OR REPLACE FUNCTION totalAge()
RETURNS integer AS $agecount$
DECLARE
agecount integer;
BEGIN
SELECT COUNT(age) into agecount FROM students;
RETURN agecount;
END;
$agecount$ LANGUAGE plpgsql;
我是在psycopg2 中操作的,在这里面创建postgreSQL 和 普通的执行SQL语句类似,首先建立连接:
1 import psycopg2 2 conn = psycopg2.connect(dsn) 3 cur = conn.cursor() 4 SQL = “““...””” # 这里放入上面的整个函数代码块 5 cur.execute(SQL) 6 conn.commit() 7 conn.close()
这就是创建的过程,这时候在pgadmin4 中去查看Schemas的public的functions中,就可以找到你刚刚创建的这个PostgreSQL 函数
调用的过程也很简单,只是把上面的SQL 里面的代码块换成:
-- SQL 代码块
SELECT totalAge()
下面给这个PostgreSQL 函数增加一些参数:
-- SQL 代码块
CREATE OR REPLACE FUNCTION columntotal( tablename varchar, columnname varchar, out columncount integer) AS $$ DECLARE ex_sql text; BEGIN ex_sql:=’SELECT COUNT(‘||quote_ident(columnname) ||’) FROM ‘ || quote_ident(tablename); execute ex_sql into columcount; END; $$ LANGUAGE plpgsql;
由上面的例子可以看出来:
1. 返回值也可以直接以参数的形式出现在postgreSQL 的形参里面的,只是需要在前面加上“out”这个标识,并且注意每个形参后面都需要指定类型。
2. 在PostgreSQL函数块里赋值的时候不是“=”,而是“:=”。
3. 当代码块中引用表名或者是字段名的时候,不能直接引用,需要使用PostgreSQL自带的“quote_ident”函数进行转换。
4. “||”这个符号的作用是作为连接符,可以将变量和字符串进行拼接,这里只说了简单的做法,具体的作用请参考:https://blog.csdn.net/fred_lzy/article/details/53188082。
5. 每段话以“;”为结尾。
调用的时候就可以通过传入表名和字段名,来查看他们的总和了,当然,只能针对整数类型的字段,如:id,age,代码块如下:
-- SQL 代码块
SELECT columntotal(‘students’, ‘id’)
-- SQL 代码块 SELECT columntotal(‘teacher’, ‘age’)
条件判断if 的使用,从参考资料中“存储过程实战2”中取一个例子进行分析,为了统一本文,修改了例子某些变量名称:
-- SQL代码块
CREATE OR REPLACE FUNCTION f_insert_table( tablename varchar, fieldname text[], fieldvalue text[], out returnValue text ) AS $$ DECLARE ex_result integer default 0; ex_sql text; BEGIN ex_sql:='insert into ' ||quote_ident(tablename) ||'(' ||array_to_string(fieldname,',') ||') values(''' ||array_to_string(fieldvalue,''',''') ||''')'; execute ex_sql; GET DIAGNOSTICS ex_result:= ROW_COUNT; if ex_result<>0 then returnValue:='{"SUCCESS":"插入操作'||ex_sql||'成功!"}'; else returnValue:='{"ERROR":"插入操作'||ex_sql||'失败!"}'; end if; END; $$ language plpgsql;
针对上面例子做下简单的分析
1. 首先这个PostgreSQL 函数接收三个参数。text[], 代表这个类型接收的是个字符串格式的数组。如‘{1,2,3,4}’,可能是他们规定的,比如我尝试传入‘[1,2,3,4]’,程序就报错提示说 Array value must start with "{" or dimension information.
2. 从字面意思上可以看出feildname这个数组肯定不止一个字段,那么进行字符串拼接的时候必须要使用自带的array_to_string,将里面的字段转换成字符串。这个函数接受两个参数,第一个参数是个数组,第二个参数是个连接符,比如上面的例子就是将feildname 中的每个元素使用‘,’拼接成一个大的字符串,再或者array_to_string(feildname,‘/’),就是将它中的每个元素使用‘/’拼接成一个大的字符串。
3. if 判断的时候,条件和执行语句中间需要加“then”关键字,而判断结束了需要加上‘end if’并且以分号‘;’结尾
函数调用:
-- SQL 代码块 SELECT f_insert_table('func_test','{name,id,time}','{qwe,123,2019-04-04}')
for 循环的使用-整数(类似于Python 中的range(int)),参考:https://blog.csdn.net/u011768325/article/details/50502490
-- SQL 代码块
CREATE OR REPLACE FUNCTION test_for( a1 integer[], a2 bigint) returns void AS $$ DECLARE ii integer; num integer; BEGIN ii:=1; num = 1; FOR ii IN 1..a2 LOOP UPDATE student SET id=a1[num] WHERE cd_id = ii; num = num +1; if (num>6) then num = 1; end if; end loop; END; $$ LANGUAGE plpgsql;
针对上面的例子,简单分析:
如果需要循环到一个整数,可以自己选择起始数值,如
从1开始到形参a2 FOR i IN 1..a2 LOOP
从100开始到形参a2 FOR i IN 100..a2 LOOP
当循环结束的时候要加上“end loop;”(注意不要少了分号“;”)
函数的调用:
-- SQL 代码块
SELECT test_for(array[1,4,5,6,7,8],6742)
循环一个可迭代对象,即对数组的遍历,分为三种情况,目前我还在学习中,
请参考:https://blog.csdn.net/yueliangdao0608/article/details/18735703
另外,经常在PostgreSQL函数 见到的$1,$2,其实就是位置参数,分别带别第一个和第二个参数
比较常用的函数定义参考:
https://blog.csdn.net/paulxin2008/article/details/41980145
https://blog.csdn.net/Wang_Dong_Liang/article/details/81334278
扩展:
1.如果不希望函数有返回值...RETURNS VOID (this defines a function with no return value) AS $$...
2.在函数代码块中,有两种注释,“--” 双减号为单行注释,“/* ...*/”为多行注释 。
3.在函数语句里,‘continue’和 ‘exit’相当于Python 中的‘continue’ 和‘break’。
4.请注意, 定义存储过程内使用的变量, 需要定义在 BEGIN 之前, 需要加 DECLARE 关键字。多个变量之间用分号分隔
5.每循环一次,循环变量自动加1;使用关键字REVERSE,循环变量自动减1。跟在IN REVERSE 后面的数字必须是从大到小的顺序,而且必须是整数,不能是变量或表达式。可以使用EXIT 退出循环。
FOR i IN 1..10 LOOP
-- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loopEND LOOP;
FOR i IN REVERSE 10..1 LOOP
-- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loopEND LOOP;
FOR i IN REVERSE 10..1 BY 2 LOOP
-- i will take on the values 10,8,6,4,2 within the loopEND LOOP;
6. 删除函数的时候, 需要传递完整的参数列表, 仅仅指定一个 函数的名称, 是无法删除的。
参考资料:
官方文档:https://www.postgresql.org/docs/10/plpgsql-declarations.html
简单易学:https://www.yiibai.com/postgresql/postgresql-functions.html
存储过程实战1:https://www.cnblogs.com/ssqhan/p/7289931.html
存储过程实战2:https://www.cnblogs.com/ssqhan/p/7399900.html