存储过程
存储过程是一组SQL语句和逻辑控制的集合。数据库系统需要支持创建、删除和修改存储过程的语法。
基本语法
示例:
匿名块
匿名块一般用于不频繁执行的脚本或不重复进行的活动。它们在一个会话中执行,并不被存储。与此相对的是命名块(也就是存储过程和函数)。
DO 语句用于执行匿名块;
嵌套子块
PL/SQL 支持代码块的嵌套,也就是将一个代码块嵌入其他代码块的主体中。
被嵌套的代码块被称为子块(subblock),包含子块的代码块被称为外部块(subblock)。
子块可以将代码进行逻辑上的拆分,子块中可以定义与外部块重名的变量,而且在子块内拥有更高的优先级。
条件语句
条件语句的主要作用是判断参数或者语句是否满足已给定的条件,根据判定结果执行相应的操作。
case 语句
也成为分支语句
简单 CASE 语句只能进行简单的等值比较。
依次判断每个 WHEN 之后的表达式,如果为真则执行相应的语句,后续的分支不再进行判断
搜索CASE语句
语句可以实现更复杂的控制逻辑。
依次判断每个 WHEN 之后的表达式,如果为真则执行相应的语句,后续的分支不再进行判断,如果没有匹配的值,执行ELSE语句。
循环语句
loop语句
游标
游标CURSOR是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括声明,OPEN,FETCH,CLOSE
其语法分别如下:
游标案例:
-- 创建示例用户表
CREATE TABLE td_user (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
age INT,
profession VARCHAR(100)
);
-- 向td_user表中插入示例数据
INSERT INTO td_user (name, age, profession) VALUES
('Alice', 25, 'Engineer'),
('Bob', 30, 'Doctor'),
('Charlie', 20, 'Artist'),
('David', 35, 'Teacher'),
('Eve', 28, 'Software Developer');
# 定义存储过程完成以下要求
# 根据传入的参数uage,来查询用户表tb_user中,所有的用户年龄小于等于uage的用户姓名(name)和专业(profession),并将用户的姓名和专业插入到所创建的一张新表(id,name,profession)中。
逻辑:
--A. 声明游标,存储过程查询结果集
--B. 准备:创建表结构
--C. 开启游标
--D. 获取游标中的记录
--E. 插入数据到新表中
--F. 关闭游标
CREATE OR REPLACE PROCEDURE p11(IN uage INT)
AS
DECLARE
uname VARCHAR(100);
upro VARCHAR(100);
CURSOR u_cursor FOR SELECT name, profession FROM td_user WHERE age <= uage;
BEGIN
-- Drop table if exists
DROP TABLE IF EXISTS tb_user_pro;
-- Create table if not exists
CREATE TABLE IF NOT EXISTS tb_user_pro (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
profession VARCHAR(100)
);
-- Open the cursor
OPEN u_cursor;
-- Loop through cursor
LOOP
FETCH u_cursor INTO uname, upro;
-- Check if no more rows to fetch
IF NOT FOUND THEN
EXIT;
END IF;
-- Insert into tb_user_pro
INSERT INTO tb_user_pro VALUES (DEFAULT, uname, upro);
END LOOP;
-- Close the cursor
CLOSE u_cursor;
END;
# 调用存储过程
CALL p11(30);
SELECT * FROM tb_user_pro;
# 案例二
CREATE TABLE emp
(
"EMPLOYEE_ID" NUMBER(10,0),
"FIRST_NAME" VARCHAR2(20),
"LAST_NAME" VARCHAR2(25) NOT NULL ENABLE,
"SALARY" NUMBER(8,2)
)
insert into emp values(100,'ticle','xie',10000);
insert into emp values(101,'tina','wang',12000);
create or replace procedure p_test (out EMPLOYEE_ID int,out FIRST_NAME varchar(20),out LAST_NAME varchar(20),out SALARY int)
is
declare
cursor mycur for
select * from emp;
empid int;
firstname varchar(20);
lastname varchar(20);
sal int;
begin
open mycur;
fetch mycur into empid,firstname,lastname,sal;
EMPLOYEE_ID:=empid;
FIRST_NAME:=firstname;
LAST_NAME:=lastname;
SALARY:=sal;
close mycur;
end
/
openGauss=# call p_test(:EMPLOYEE_ID,:FIRST_NAME,:LAST_NAME,:SALARY);
employee_id | first_name | last_name | salary
-------------+------------+-----------+--------
100 | ticle | xie | 10000
(1 row)
# 删除员工表hr.staffs表中某部门的所有员工,如果该部门中已没有员工,则在部门表hr.sections中删除该部门。
CREATE OR REPLACE PROCEDURE proc_cursor3()
AS
DECLARE
V_DEPTNO NUMBER(4) := 100;
BEGIN
DELETE FROM hr.staffs WHERE section_ID = V_DEPTNO;
--根据游标状态做进一步处理
IF SQL%NOTFOUND THEN
DELETE FROM hr.sections WHERE section_ID = V_DEPTNO;
END IF;
END;
/
CALL proc_cursor3(); 错误捕获
默认情况下,存储过程遇到错误时会终止代码执行,同时撤销事务。在代码块中使用EXCEPTION捕获错误并继续事务。
如果代码执行出错,程序将会进入 EXCEPTION 模块。
更多的存储过程循环案例去官方文档 SQL参考 找到存储过程 或直接搜索循环
存储过程
存储过程是一组SQL语句和逻辑控制的集合。数据库系统需要支持创建、删除和修改存储过程的语法。
基本语法
CREATE [OR REPLACE] PROCEDURE 存储过程名( [IN|OUT] 参数1数据类型, [IN|OUT] 参数2数据类型, ... ) AS DECLARE 变量1数据类型, 变量2数据类型, ... BEGIN 存储过程的程序体 END; # IN参数是存储过程的输入参数,它将存储过程外部的值传递给存储过程使用。 # OUT参数是存储过程的输出参数,存储过程在执行时,会将执行的中间结果赋值给OUT参数,存储过程执行完毕后,外部用户可以通过OUT参数获得存储过程的执行结果。 # INOUT参数则同时具有IN参数和OUT参数的性质,它既是存储过程的输入参数,同时在存储过程执行中也会通过INOUT参数将中间结果输出给外部用户示例:
匿名块
匿名块一般用于不频繁执行的脚本或不重复进行的活动。它们在一个会话中执行,并不被存储。与此相对的是命名块(也就是存储过程和函数)。
DO 语句用于执行匿名块;
嵌套子块
PL/SQL 支持代码块的嵌套,也就是将一个代码块嵌入其他代码块的主体中。
被嵌套的代码块被称为子块(subblock),包含子块的代码块被称为外部块(subblock)。
子块可以将代码进行逻辑上的拆分,子块中可以定义与外部块重名的变量,而且在子块内拥有更高的优先级。
条件语句
条件语句的主要作用是判断参数或者语句是否满足已给定的条件,根据判定结果执行相应的操作。
# 匿名块条件语句示例 比较两数大小 openGauss=# DO $$ DECLARE i integer := 4; j integer := 5; BEGIN IF i > j THEN RAISE NOTICE 'i 大于 j'; ELSIF i < j THEN RAISE NOTICE 'i 小于 j'; ELSE RAISE NOTICE 'i 等于 j'; END IF; END $$; ENDNOTICE: i 小于 j ANONYMOUS BLOCK EXECUTE # 存储过程示例 判断这个数是否大于0 CREATE OR REPLACE PROCEDURE proc_control_structure(i in integer) AS BEGIN IF i > 0 THEN raise info 'i:% is greater than 0. ',i; ELSIF i < 0 THEN raise info 'i:% is smaller than 0. ',i; ELSE raise info 'i:% is equal to 0. ',i; END IF; RETURN; END; / CALL proc_control_structure(3);case 语句
也成为分支语句
简单 CASE 语句只能进行简单的等值比较。
依次判断每个 WHEN 之后的表达式,如果为真则执行相应的语句,后续的分支不再进行判断
openGauss=#CREATE OR REPLACE PROCEDURE proc_case_branch(pi_result in integer, pi_return out integer) AS BEGIN CASE pi_result WHEN 1 THEN pi_return := 111; WHEN 2 THEN pi_return := 222; WHEN 3 THEN pi_return := 333; WHEN 6 THEN pi_return := 444; END CASE; raise info 'pi_return : %',pi_return ; END; / finance=# call proc_case_branch(3,0); INFO: pi_return : 333 pi_return ----------- 333 (1 row)搜索CASE语句
语句可以实现更复杂的控制逻辑。
依次判断每个 WHEN 之后的表达式,如果为真则执行相应的语句,后续的分支不再进行判断,如果没有匹配的值,执行ELSE语句。
openGauss=# DO $$ DECLARE i integer := 3; BEGIN CASE WHEN i BETWEEN 1 AND 5 THEN RAISE NOTICE '数据在1~5之间'; WHEN i BETWEEN 6 AND 10 THEN RAISE NOTICE '数据在6~10之间'; ELSE RAISE NOTICE 'other value'; END CASE; END $$; NOTICE: 数据在1~5之间 ANONYMOUS BLOCK EXECUTE # 存储过程 CREATE OR REPLACE PROCEDURE check_range() AS DECLARE i INTEGER := 3; BEGIN CASE WHEN i BETWEEN 1 AND 5 THEN RAISE NOTICE '数据在1~5之间'; WHEN i BETWEEN 6 AND 10 THEN RAISE NOTICE '数据在6~10之间'; ELSE RAISE NOTICE 'other value'; END CASE; END; / # 自己输入参数 CREATE OR REPLACE PROCEDURE check_range(in_num INTEGER) AS BEGIN CASE WHEN in_num BETWEEN 1 AND 5 THEN RAISE NOTICE '数据在1~5之间'; WHEN in_num BETWEEN 6 AND 10 THEN RAISE NOTICE '数据在6~10之间'; ELSE RAISE NOTICE 'other value'; END CASE; END; /循环语句
loop语句
# 简单的LOOP CREATE OR REPLACE PROCEDURE proc_loop(i in integer, count out integer) AS BEGIN count:=0; LOOP IF count > i THEN raise info 'count is %. ', count; EXIT; ELSE count:=count+1; END IF; END LOOP; END; CALL proc_loop(10,5); # while_loop 语句 # 只要条件表达式为真,WHILE语句就会不停的在一系列语句上进行循环,在每次进入循环体的时候进行条件判断。游标
游标CURSOR是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括声明,OPEN,FETCH,CLOSE
其语法分别如下:
游标案例:
-- 创建示例用户表 CREATE TABLE td_user ( id SERIAL PRIMARY KEY, name VARCHAR(100), age INT, profession VARCHAR(100) ); -- 向td_user表中插入示例数据 INSERT INTO td_user (name, age, profession) VALUES ('Alice', 25, 'Engineer'), ('Bob', 30, 'Doctor'), ('Charlie', 20, 'Artist'), ('David', 35, 'Teacher'), ('Eve', 28, 'Software Developer'); # 定义存储过程完成以下要求 # 根据传入的参数uage,来查询用户表tb_user中,所有的用户年龄小于等于uage的用户姓名(name)和专业(profession),并将用户的姓名和专业插入到所创建的一张新表(id,name,profession)中。 逻辑: --A. 声明游标,存储过程查询结果集 --B. 准备:创建表结构 --C. 开启游标 --D. 获取游标中的记录 --E. 插入数据到新表中 --F. 关闭游标 CREATE OR REPLACE PROCEDURE p11(IN uage INT) AS DECLARE uname VARCHAR(100); upro VARCHAR(100); CURSOR u_cursor FOR SELECT name, profession FROM td_user WHERE age <= uage; BEGIN -- Drop table if exists DROP TABLE IF EXISTS tb_user_pro; -- Create table if not exists CREATE TABLE IF NOT EXISTS tb_user_pro ( id SERIAL PRIMARY KEY, name VARCHAR(100), profession VARCHAR(100) ); -- Open the cursor OPEN u_cursor; -- Loop through cursor LOOP FETCH u_cursor INTO uname, upro; -- Check if no more rows to fetch IF NOT FOUND THEN EXIT; END IF; -- Insert into tb_user_pro INSERT INTO tb_user_pro VALUES (DEFAULT, uname, upro); END LOOP; -- Close the cursor CLOSE u_cursor; END; # 调用存储过程 CALL p11(30); SELECT * FROM tb_user_pro; # 案例二 CREATE TABLE emp ( "EMPLOYEE_ID" NUMBER(10,0), "FIRST_NAME" VARCHAR2(20), "LAST_NAME" VARCHAR2(25) NOT NULL ENABLE, "SALARY" NUMBER(8,2) ) insert into emp values(100,'ticle','xie',10000); insert into emp values(101,'tina','wang',12000); create or replace procedure p_test (out EMPLOYEE_ID int,out FIRST_NAME varchar(20),out LAST_NAME varchar(20),out SALARY int) is declare cursor mycur for select * from emp; empid int; firstname varchar(20); lastname varchar(20); sal int; begin open mycur; fetch mycur into empid,firstname,lastname,sal; EMPLOYEE_ID:=empid; FIRST_NAME:=firstname; LAST_NAME:=lastname; SALARY:=sal; close mycur; end / openGauss=# call p_test(:EMPLOYEE_ID,:FIRST_NAME,:LAST_NAME,:SALARY); employee_id | first_name | last_name | salary -------------+------------+-----------+-------- 100 | ticle | xie | 10000 (1 row) # 删除员工表hr.staffs表中某部门的所有员工,如果该部门中已没有员工,则在部门表hr.sections中删除该部门。 CREATE OR REPLACE PROCEDURE proc_cursor3() AS DECLARE V_DEPTNO NUMBER(4) := 100; BEGIN DELETE FROM hr.staffs WHERE section_ID = V_DEPTNO; --根据游标状态做进一步处理 IF SQL%NOTFOUND THEN DELETE FROM hr.sections WHERE section_ID = V_DEPTNO; END IF; END; / CALL proc_cursor3();错误捕获
默认情况下,存储过程遇到错误时会终止代码执行,同时撤销事务。在代码块中使用EXCEPTION捕获错误并继续事务。
如果代码执行出错,程序将会进入 EXCEPTION 模块。
openGauss=# CREATE OR REPLACE PROCEDURE proc_exp() AS DECLARE x INT :=0; y INT; BEGIN UPDATE employees SET first_name = 'James' WHERE first_name = 'John'; x := x + 1; y := x / 0; EXCEPTION WHEN division_by_zero THEN RAISE NOTICE 'caught division_by_zero'; RAISE NOTICE 'x: %',x; RETURN ; END; / CREATE PROCEDURE openGauss=# call proc_exp(); NOTICE: caught division_by_zero NOTICE: x: 1更多的存储过程循环案例去官方文档 SQL参考 找到存储过程 或直接搜索循环