存储过程是一组可以完成特定功能的SQL语句集合,经编译后存储在数据库中。存储过程的执行效率比逐条执行的SQL语句高很多,因为普通的SQL语句,每次都会对SQL进行解析、编译、执行,而存储过程只是在第一次执行时进行解析、编译、执行,以后都是对结果进行调用。
存储过程解析工作就是在SOL引擎模块中完成。
CREATE [ OR REPLACE ] [DEFINER = user] PROCEDURE procedure_name
[ ( {[ argname ] [ argmode ] argtype [ { DEFAULT | := | = } expression ]}[,...]) ]
{ IS | AS }
BEGIN
procedure_body
END
/
CREATE OR REPLACE PROCEDURE proc_test(a int,b out int)
AS
BEGIN
b=a;
END;
/
call proc_test(3,0);
虽然第二个参数是输出参数,但是在调用存储过程的时候也需要传值,否则会报错
正常调用执行结果如下所示:
DROP PROCEDURE proc_test;
在存储过程编写过程中可能会出现需要根据不同情况执行不同操作的情况,那么此时可以使用流程控制即判断来实现,openGauss数据库支持if…else if…else…end if也支持case when…then…end case完成流程控制。
CREATE OR REPLACE PROCEDURE proc_if(a int,b out int)
AS
BEGIN
if a<0 then
b=-1;
else if a=0 then -- else if可以是0或多个
b=0;
else
b=1;
end if;
end if;
END;
/
使用了几次if最终判断结束的时候就需要几个end if
CREATE OR REPLACE PROCEDURE proc_case(a int,b out int)
AS
BEGIN
case when a<0 then
b=-1;
when a=0 then
b=0;
else
b=1;
end case;
END;
/
在存储过程中如果想要分别获取数据集中的数据,此时就需要循环遍历数据集,或需要重复执行相同操作也可以使用循环实现,openGauss数据支持的循环有for、while、loop
CREATE TABLE t2(c1 int); – 创建一张新表用于测试
CREATE OR REPLACE PROCEDURE proc_for(startnum int,endnum int)
AS
BEGIN
for i in startnum .. endnum loop
insert into t2 values(i);
end loop;
END;
/
startnum … endnum相当于生成了一个startnum开始到endnum结束的整数序列,如果startnum=1,endnum=10,那么此时整数序列为1-10(包含10),一共循环10次,i的取值分别是1,2,3,4,5,6,7,8,9,10
CREATE OR REPLACE PROCEDURE proc_while(startnum int,endnum int)
AS
declare a int;
BEGIN
a=startnum;
while a<=endnum loop -- 当a>endnum时循环结束
insert into t2 values(a);
a=a+1; -- 每循环一次a+1
end loop;
END;
/
CREATE OR REPLACE PROCEDURE proc_loop(startnum int,endnum int)
AS
declare a int;
BEGIN
a=startnum;
loop
if a>endnum then -- 当a>endnum时,exit退出循环
exit;
end if;
insert into t2 values(a);
a=a+1; -- 每循环一次a+1
end loop;
END;
/
当确定查询的内容仅一行一列时,可以将该单元格的数据赋值给输出参数进行输出
CREATE TABLE t1(c1 int,c2 varchar(20));
INSERT INTO t1 VALUES(1001,'zhangsan'),(1002,'lisi');
CREATE OR REPLACE PROCEDURE proc_test(b out varchar)
AS
BEGIN
select c2 into b from t1 where c1=1001; -- into将c2的值赋值给输出参数b
END;
/
call test_pro('');
查询结果如下:
当查询一个单元格的数据是可以使用输出参数,但当查询多行/多列/多行多列时需要借助于循环进行遍历。可以使用游标也可以不使用
CREATE OR REPLACE PROCEDURE proc_selectData()
AS
BEGIN
for i in (select * from t1) loop -- 循环遍历查询结果集
raise notice 'ID:%,NAME:%',i.c1,i.c2; -- 输出
end loop;
END;
/
raise notice表示已notice日志的形式输出数据,输出字符串
'%,%'就是要输出的字符串,%为占位符,字符串后的第一个值传给第一个占位符,依次赋值。
CREATE OR REPLACE PROCEDURE proc_selectCur()
AS
declare cursor cur1 is select * from t1; -- 定义游标
BEGIN
for i in cur1 loop -- 循环遍历游标
raise notice '%,%',i.c1,i.c2; -- 输出
end loop;
END;
/
for会自动打开并关闭游标,不需要用户操作游标的打开、取值、关闭。
CREATE OR REPLACE PROCEDURE proc_selectCur2()
AS
declare cursor cur1 is select * from t1; -- 定义游标
declare data record; -- 用于接收游标中的记录
BEGIN
open cur1; -- 打开游标
loop
if cur1%notfound then -- 判断游标是否结束
close cur1; -- 关闭游标
exit; -- 跳出循环
end if;
fetch next from cur1 into data; -- 遍历游标(取下一个值)
raise notice '%,%',data.c1,data.c2; -- 输出
end loop;
END;
/
自定义函数的整体语法与存储过程类似,但有些区别:
(1)数据库默认兼容的是Oracle,所以默认自定义函数必须要有返回值,存储过程不需要返回值。
(2)数据库兼容模式为Oracle时,自定义函数支持IN/OUT/INOUT模式的参数。
(3)存储过程的调用使用call,自定义函数的调用使用select。
(4)自定义函数可用于触发器。
CREATE [ OR REPLACE ] [DEFINER = user] FUNCTION function_name
( [ { argname [ argmode ] argtype [ { DEFAULT | := | = } expression ] } [, ...] ] )
RETURNS rettype [ DETERMINISTIC ] [...]
{ IS | AS }
$$
BEGIN
plsql_body
RETURN value;
END
$$LANGUAGE PLPGSQL
CREATE OR REPLACE FUNCTION func_test() RETURNS int
AS
$$
declare a int; -- 定义变量
BEGIN
a=1;
return a;
END;
$$LANGUAGE PLPGSQL;
select func_test();
DROP FUNCTION func_test();
CREATE OR REPLACE FUNCTION func_trigger() RETURNS TRIGGER
AS
$$
BEGIN
.....(处理代码)
return null;
END;
$$LANGUAGE PLPGSQL;
自定义函数的整体语法与存储过程类似,但有些区别:
(1)数据库默认兼容的是Oracle,所以默认自定义函数必须要有返回值,存储过程不需要返回值。
(2)数据库兼容模式为Oracle时,自定义函数支持IN/OUT/INOUT模式的参数。
(3)存储过程的调用使用call,自定义函数的调用使用select。
(4)自定义函数可用于触发器。
CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
ON table_name
[ FROM referenced_table_name ]
{ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } }
[ FOR [ EACH ] { ROW | STATEMENT } ]
[ WHEN ( condition ) ]
EXECUTE PROCEDURE function_name ( arguments )
创建两张表t1和t2用于测试
CREATE TABLE t1(c1 int);
CREATE TABLE t2(c1 int);
触发器所需要的函数定义
CREATE OR REPLACE FUNCTION func_insert() RETURNS trigger
AS
$$
BEGIN
insert into t2 values(new.c1); -- new为当前插入的新纪录,new.c1就是新纪录的c1列
return null; -- 作用于触发器的函数不需要返回结果,但必须有return所以返回null
END;
$$LANGUAGE PLPGSQL;
触发器定义
CREATE TRIGGER insert_t1_t2() AFTER INSERT ON t1 FOR EACH ROW EXECUTE func_insert();
insert可以批量插入多条记录,为了保证插入t1表的每条数据都能插入t2表,所以选择FOR EACH ROW。
触发器所需要的函数定义
CREATE OR REPLACE FUNCTION func_delete() RETURNS trigger
AS
$$
BEGIN
delete from t2 where c1=old.c1; -- old为当前操作的旧记录,old.c1为当前记录的c1列
return null; -- 作用于触发器的函数不需要返回结果,但必须有return所以返回null
END;
$$LANGUAGE PLPGSQL;
触发器定义
CREATE TRIGGER delete_t1_t2() BEFORE DELETE ON t1 FOR EACH ROW EXECUTE func_delete();
如果触发器处理更改为:当用户删除t1表数据时,同步删除t2的数据,t1表保留不动
CREATE TRIGGER delete_t1_t2() INSTEAD OF DELETE ON t1 FOR EACH ROW EXECUTE func_delete();
触发器所需要的函数定义
CREATE OR REPLACE FUNCTION func_update() RETURNS trigger
AS
$$
BEGIN
update t2 set c1=new.c1 where c1=old.c1; -- new.c1就是更改后的值,old.c1为更改前的
return null; -- 作用于触发器的函数不需要返回结果,但必须有return所以返回null
END;
$$LANGUAGE PLPGSQL;
触发器定义
CREATE TRIGGER update_t1_t2() AFTER UPDATE ON t1 FOR EACH ROW EXECUTE func_update();
如果需要指定触发器仅在更改c1列时才会被触发,配置如下:
CREATE TRIGGER insert_t1_t2() AFTER UPDATE OF c1 ON t1 FOR EACH ROW EXECUTE func_update();