@SovietPower
2022-05-06T16:58:11.000000Z
字数 18166
阅读 1562
DB
https://opengauss.org/zh/docs/3.0.0/docs/Developerguide/%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84.html
创建函数格式如下。[lang]
根据语法可选plpgsql
或sql
。
注意最后还需要一个;
结束语句。
如果函数体只需一条语句,则使用sql
可删掉declare begin end
。
create or replace function public.func (arg varchar(20))
returns integer as $$
declare
begin
end;
$$ LANGUAGE [lang];
创建过程。
注意最后还需要一个换行/
结束语句。
create or replace procedure public.proc (x in varchar(20), y out integer) as
begin
select count(*) into d_count from instructor
where instructor.dept_name = d_name;
end;
/
运行创建的容器。
PS H:\> docker start opengauss
PS H:\> docker exec -it opengauss bash
root@97375c3acbd1:/# su omm
omm@97375c3acbd1:/$ gsql -d db2022 -r
gsql ((openGauss 2.1.0 build 590b0f8e) compiled at 2021-09-30 14:29:04 commit 0 last mr )
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help.
db2022=#
4. 创建dept_count
统计指定院系的教师数量。
db2022=# create or replace function public.dept_count (d_name varchar(20))
db2022-# returns integer
db2022-# LANGUAGE plpgsql
db2022-# as $$
db2022$# declare
db2022$# res integer;
db2022$# begin
db2022$# select count(*) into res from instructor where dept_name=d_name;
db2022$# return res;
db2022$# end; $$;
CREATE FUNCTION
5. 执行dept_count
db2022=# select dept_name, budget
db2022-# from department
db2022-# where dept_count(dept_name)>4;
dept_name | budget
------------+-----------
Athletics | 734550.70
Statistics | 395051.74
(2 rows)
6. 创建instructor_of
返回指定院系中的所有教师的信息。返回一张表。
db2022=# create or replace function public.instructor_of (d_name varchar(20))
db2022-# returns table(
db2022(# ID varchar(5),
db2022(# name varchar(20),
db2022(# dept_name varchar(20),
db2022(# salary numeric(8,2)
db2022(# )
db2022-# LANGUAGE sql as $$
db2022$# select ID, name, dept_name, salary from instructor
db2022$# where instructor.dept_name=d_name;
db2022$# $$;
CREATE FUNCTION
7. 调用instructor_of
db2022=# select * from instructor_of('Finance');
id | name | dept_name | salary
------+--------+-----------+-----------
6569 | Mingoz | Finance | 115311.38
(1 row)
8. 创建dept_count_proc
db2022=# create procedure public.dept_count_proc (d_name in varchar(20), d_count out integer) as
db2022$# begin
db2022$# select count(*) into d_count from instructor
db2022$# where instructor.dept_name = d_name;
db2022$# end;
db2022$# /
CREATE PROCEDURE
9. 调用dept_count_proc
db2022=# call dept_count_proc('Physics', d_count);
d_count
---------
2
(1 row)
10. 创建register_student
为学生注册课程。
create or replace procedure public.register_student (
s_id in varchar(5), s_course_id in varchar(8),
s_sec_id in varchar(8), s_semester in varchar(6),
s_year in numeric(4,0), msg out varchar(100)
) as
declare
enrollment integer;
lim integer;
begin
select count(*) into enrollment from takes
where course_id = s_course_id and sec_id = s_sec_id and semester=s_semester and year=s_year;
select capacity into lim from classroom natural join section
where course_id = s_course_id and sec_id = s_sec_id and semester=s_semester and year=s_year;
if (enrollment < lim) then
begin
insert into takes values(s_id, s_course_id, s_semester, s_year, null);
msg := 'Successful!';
end;
else
msg := 'Enrollment limit reached for course' || s_course_id || ' section' || s_sec_id;
end if;
end;
/
db2022=# create or replace procedure public.register_student (
db2022(# s_id in varchar(5), s_course_id in varchar(8),
db2022(# s_sec_id in varchar(8), s_semester in varchar(6),
db2022(# s_year in numeric(4,0), msg out varchar(100)
db2022(# ) as
db2022$# declare
db2022$# enrollment integer;
db2022$# lim integer;
db2022$# begin
db2022$# select count(*) into enrollment from takes
db2022$# where course_id = s_course_id and sec_id = s_sec_id and semester=s_semester and year=s_year;
db2022$# select capacity into lim from classroom natural join section
db2022$# where course_id = s_course_id and sec_id = s_sec_id and semester=s_semester and year=s_year;
db2022$# if (enrollment < lim) then
db2022$# begin
db2022$# insert into takes values(s_id, s_course_id, s_semester, s_year, null);
db2022$# msg := 'Successful!';
db2022$# end;
db2022$# else
db2022$# msg := 'Enrollment limit reached for course' || s_course_id || ' section' || s_sec_id;
db2022$# end if;
db2022$# end;
db2022$# /
CREATE PROCEDURE
11. 调用register_student
db2022=# call register_student('1018','169','1','Spring','2007',msg);
msg
------------------------------------------------
Enrollment limit reached for course169 section1
(1 row)
db2022=# call register_student('1018','258','1','Fall','2007',msg);
msg
------------------------------------------------
Enrollment limit reached for course258 section1
(1 row)
12. 创建test_proc
为该课程在新学期开设一个新的section。该课程所属院系的学生如果之前没有修过该课程,则为其注册该课程。
create or replace procedure public.test_proc (cid in varchar(8), sec in varchar(8), sem varchar(6), y numeric(4,0)) as
declare
dept varchar(20);
cred numeric(2,0);
begin
create temporary table stu(id varchar(5));
insert into section values(cid, sec, sem, y, null, null, null);
select dept_name into dept from course where course_id=cid;
select credits into cred from course where course_id=cid;
insert into stu
(select ID from student where dept_name=dept)
except
(select ID from student natural join takes
where dept_name=dept and course_id=cid);
insert into takes
select ID, cid, sec, sem, y, null
from stu;
update student
set tot_cred = tot_cred+cred
where student.ID in (select ID from stu);
end;
/
db2022=# create or replace procedure public.test_proc (cid in varchar(8), sec in varchar(8), sem varchar(6), y numeric(4,0)) as
db2022$# declare
db2022$# dept varchar(20);
db2022$# cred numeric(2,0);
db2022$# begin
db2022$# create temporary table stu(id varchar(5));
db2022$# insert into section values(cid, sec, sem, y, null, null, null);
db2022$# select dept_name into dept from course where course_id=cid;
db2022$# select credits into cred from course where course_id=cid;
db2022$# insert into stu
db2022$# (select ID from student where dept_name=dept)
db2022$# except
db2022$# (select ID from student natural join takes
db2022$# where dept_name=dept and course_id=cid);
db2022$# insert into takes
db2022$# select ID, cid, sec, sem, y, null
db2022$# from stu;
db2022$# update student
db2022$# set tot_cred = tot_cred+cred
db2022$# where student.ID in (select ID from stu);
db2022$# end;
db2022$# /
CREATE PROCEDURE
db2022=# select * from takes where id='52157';
id | course_id | sec_id | semester | year | grade
-------+-----------+--------+----------+------+-------
...
52157 | 747 | 1 | Spring | 2004 | A+
52157 | 338 | 1 | Spring | 2007 | A-
(18 rows)
db2022=# select * from student where id='52157';
id | name | dept_name | tot_cred
-------+-------+-----------+----------
52157 | Nagle | Astronomy | 62
(1 row)
db2022=# call test_proc('489', '1', 'Spring', 2022);
test_proc
-----------
(1 row)
db2022=# select * from takes where id='52157';
id | course_id | sec_id | semester | year | grade
-------+-----------+--------+----------+------+-------
...
52157 | 747 | 1 | Spring | 2004 | A+
52157 | 338 | 1 | Spring | 2007 | A-
52157 | 489 | 1 | Spring | 2022 |
(19 rows)
db2022=# select * from student where id='52157';
id | name | dept_name | tot_cred
-------+-------+-----------+----------
52157 | Nagle | Astronomy | 66
(1 row)
与其它数据库管理系统不同,openGauss在创建触发器时并不直接包含所执行的处理逻辑,而是将处理逻辑放在事先创建的不
带参数且返回类型为TRIGGER的函数中,在触发器的定义中指定调用该函数。
openGauss不支持函数中使用COMMIT/ROLLBACK/SAVEPOINT(包括函数调用含有COMMIT/ROLLBACK/SAVEPOINT的存储过程)。
openGauss不支持TRIGGER中调用含有COMMIT/ROLLBACK/SAVEPOINT语句的存储过程。
13. 为触发器创建函数 tri_insert_section_func 和 tri_delete_timeslot_func
create or replace function public.tri_insert_section_func ()
returns trigger
not fenced not shippable as $$
declare
begin
if new.time_slot_id not in (select time_slot_id from time_slot) then
delete from section
where course_id=new.course_id and sec_id=new.sec_id and semester=new.semester and year=new.year;
raise exception 'time_slot table中不存在指定的time_slot_it,insert失败.';
end if;
return new;
end;
$$ LANGUAGE plpgsql;
create or replace function public.tri_delete_timeslot_func ()
returns trigger
not fenced not shippable as $$
declare
begin
if old.time_slot_id not in (select time_slot_id from time_slot) and old.time_slot_id in (select time_slot_id from section) then
insert into time_slot values(old.time_slot_id, old.day, old.start_hr, old.start_min, old.end_hr, old.end_min);
raise exception 'time_slot_id仍被section引用,delete失败.';
end if;
return old;
end;
$$ LANGUAGE plpgsql;
db2022=# create or replace function public.tri_insert_section_func ()
db2022-# returns trigger
db2022-# not fenced not shippable as $$
db2022$# declare
db2022$#
db2022$# begin
db2022$# if new.time_slot_id not in (select time_slot_id from time_slot) then
db2022$# delete from section
db2022$# where course_id=new.course_id and sec_id=new.sec_id and semester=new.semester and year=new.year;
db2022$# raise exception 'time_slot table中不存在指定的time_slot_it,insert失败.';
db2022$# end if;
db2022$# return new;
db2022$# end;
db2022$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
db2022=# create or replace function public.tri_delete_timeslot_func ()
db2022-# returns trigger
db2022-# not fenced not shippable as $$
db2022$# declare
db2022$#
db2022$# begin
db2022$# if old.time_slot_id not in (select time_slot_id from time_slot) and old.time_slot_id in (select time_slot_id from section) then
db2022$# insert into time_slot values(old.time_slot_id, old.day, old.start_hr, old.start_min, old.end_hr, old.end_min);
db2022$# raise exception 'time_slot_id仍被section引用,delete失败.';
db2022$# end if;
db2022$# return old;
db2022$# end;
db2022$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
14. 创建触发器 insert_section_timeslot_check 和 delete_timeslot_check
db2022=# create trigger insert_section_timeslot_check after insert on section
db2022-# for each row
db2022-# execute procedure tri_insert_section_func();
CREATE TRIGGER
db2022=# create trigger delete_timeslot_check after delete on time_slot
db2022-# for each row
db2022-# execute procedure tri_delete_timeslot_func();
CREATE TRIGGER
15. 检查触发器 insert_section_timeslot_check 效果
db2022=# select * from time_slot where time_slot_id = 'Q';
time_slot_id | day | start_hr | start_min | end_hr | end_min
--------------+-----+----------+-----------+--------+---------
(0 rows)
db2022=# insert into section values('747','1','Fall',2021,'Gates','314','Q');
ERROR: time_slot table中不存在指定的time_slot_it,insert失败.
db2022=# select * from section where course_id = '747' and sec_id = '1' and semester = 'Fall' and year = 2021;
course_id | sec_id | semester | year | building | room_number | time_slot_id
-----------+--------+----------+------+----------+-------------+--------------
(0 rows)
db2022=# insert into time_slot values('Q','W',10,0,12,30);
INSERT 0 1
db2022=# insert into section values('747','1','Fall',2021,'Gates','314','Q');
INSERT 0 1
db2022=# select * from section where course_id = '747' and sec_id = '1' and semester = 'Fall' and year = 2021;
course_id | sec_id | semester | year | building | room_number | time_slot_id
-----------+--------+----------+------+----------+-------------+--------------
747 | 1 | Fall | 2021 | Gates | 314 | Q
(1 row)
16. 检查触发器 delete_timeslot_check 效果
db2022=# insert into time_slot values('Q','F',10,0,12,30);
INSERT 0 1
db2022=# select * from time_slot where time_slot_id = 'Q';
time_slot_id | day | start_hr | start_min | end_hr | end_min
--------------+-----+----------+-----------+--------+---------
Q | W | 10 | 0 | 12 | 30
Q | F | 10 | 0 | 12 | 30
(2 rows)
db2022=# delete from time_slot where time_slot_id = 'Q' and day = 'W';
DELETE 1
db2022=# select * from time_slot where time_slot_id = 'Q';
time_slot_id | day | start_hr | start_min | end_hr | end_min
--------------+-----+----------+-----------+--------+---------
Q | F | 10 | 0 | 12 | 30
(1 row)
db2022=# delete from time_slot where time_slot_id = 'Q' and day = 'F';
ERROR: time_slot_id仍被section引用,delete失败.
db2022=# select * from time_slot where time_slot_id = 'Q';
time_slot_id | day | start_hr | start_min | end_hr | end_min
--------------+-----+----------+-----------+--------+---------
Q | F | 10 | 0 | 12 | 30
(1 row)
db2022=# select * from section where time_slot_id = 'Q';
course_id | sec_id | semester | year | building | room_number | time_slot_id
-----------+--------+----------+------+----------+-------------+--------------
747 | 1 | Fall | 2021 | Gates | 314 | Q
(1 row)
db2022=# delete from section where time_slot_id = 'Q';
DELETE 1
db2022=# delete from time_slot where time_slot_id = 'Q' and day = 'F';
DELETE 1
db2022=# select * from time_slot where time_slot_id = 'Q';
time_slot_id | day | start_hr | start_min | end_hr | end_min
--------------+-----+----------+-----------+--------+---------
(0 rows)
17. 删除创建的触发器和函数
db2022=# DROP TRIGGER delete_timeslot_check on time_slot;
DROP TRIGGER
db2022=# DROP TRIGGER insert_section_timeslot_check on section;
DROP TRIGGER
db2022=# DROP FUNCTION IF EXISTS tri_insert_section_func;
DROP FUNCTION
db2022=# DROP FUNCTION IF EXISTS tri_delete_timeslot_func;
DROP FUNCTION
18. 创建触发器 tri_update_credits
用于维护学生所获得的学分。
create or replace function public.on_update_credits_func ()
returns trigger
not fenced not shippable as $$
begin
if new.grade<>'F' and new.grade is not null and (old.grade='F' or old.grade is null) then
update student
set tot_cred = tot_cred+
(select credits from course where course.course_id=new.course_id);
end if;
return new;
end;
$$ LANGUAGE plpgsql;
create trigger on_update_credits after update on takes
for each row
execute procedure on_update_credits_func();
db2022=# create or replace function public.on_update_credits_func ()
db2022-# returns trigger
db2022-# not fenced not shippable as $$
db2022$# begin
db2022$# if new.grade<>'F' and new.grade is not null and (old.grade='F' or old.grade is null) then
db2022$# update student
db2022$# set tot_cred = tot_cred+
db2022$# (select credits from course where course.course_id=new.course_id);
db2022$# end if;
db2022$# return new;
db2022$# end;
db2022$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
db2022=# create trigger on_update_credits after update on takes
db2022-# for each row
db2022-# execute procedure on_update_credits_func();
CREATE TRIGGER
db2022=# select * from takes where grade='F';
id | course_id | sec_id | semester | year | grade
-------+-----------+--------+----------+------+-------
79446 | 808 | 1 | Fall | 2003 | F
99710 | 169 | 2 | Fall | 2002 | F
...
db2022=# select * from student where id='99710';
id | name | dept_name | tot_cred
-------+------------+-----------+----------
99710 | Savolainen | Languages | 50
(1 row)
db2022=# update takes set grade='A' where id='99710' and course_id='169' and sec_id='2';
UPDATE 1
db2022=# select * from student where id='99710';
id | name | dept_name | tot_cred
-------+------------+-----------+----------
99710 | Savolainen | Languages | 53
(1 row)
19. 创建视图 rec_prereq
找出所有课程的直接和间接前导课程。
db2022=# create view rec_prereq as
db2022-# with recursive rec_prereq(course_id, prereq_id) as (
db2022(# select course_id, prereq_id
db2022(# from prereq
db2022(# union
db2022(# select rec_prereq.course_id, prereq.prereq_id
db2022(# from rec_prereq, prereq
db2022(# where rec_prereq.prereq_id = prereq.course_id
db2022(# )
db2022-# select * from rec_prereq;
CREATE VIEW
db2022=# select * from rec_prereq where course_id='760';
course_id | prereq_id
-----------+-----------
760 | 169
760 | 603
760 | 735
(3 rows)
20. 创建视图 students_gpa
显示学生的GPA。
db2022=# create view students_gpa as
db2022-# select id, name, round(sum(point*credits)/sum(credits), 1) as GPA
db2022-# from student natural left outer join (
db2022(# select id, takes.course_id, course.credits, max(point) as point
db2022(# from takes, grade_point, course
db2022(# where trim(takes.grade) = grade_point.grade and takes.course_id = course.course_id
db2022(# group by id, takes.course_id, course.credits
db2022(# ) group by id, name;
CREATE VIEW
db2022=# select * from students_gpa where name='Cox';
id | name | gpa
-------+------+-----
59908 | Cox | 2.8
21766 | Cox | 3.0
44304 | Cox | 3.4
(3 rows)
21. 查询学生的GPA排名
db2022=# select ID, name, gpa, rank() over (order by (GPA) desc) as s_rank
db2022-# from students_gpa
db2022-# limit 5;
id | name | gpa | s_rank
-------+---------+-----+--------
79446 | Frost | | 1
56499 | Zarpell | 3.9 | 2
81896 | Feldman | 3.9 | 2
18286 | Pang | 3.9 | 2
98619 | Nagaraj | 3.9 | 2
(5 rows)
db2022=# select ID, name, rank() over (order by GPA desc nulls last) as s_rank
db2022-# from students_gpa
db2022-# limit 5;
id | name | s_rank
-------+---------+--------
98619 | Nagaraj | 1
18286 | Pang | 1
56499 | Zarpell | 1
81896 | Feldman | 1
4248 | Wright | 5
(5 rows)
db2022=# select ID, (1 + (select count(*) from students_gpa B where B.GPA > A.GPA)) as s_rank
db2022-# from students_gpa A
db2022-# order by s_rank
db2022-# limit 5;
id | s_rank
-------+--------
81896 | 1
79446 | 1
18286 | 1
98619 | 1
56499 | 1
(5 rows)
22. 创建视图 dept_grades
显示学生的GPA,包含其院系。
db2022=# create view dept_grades as
db2022-# select id, name, dept_name, round(sum(point*credits)/sum(credits), 1) as GPA
db2022-# from student natural left outer join (
db2022(# select id, takes.course_id, course.credits, max(point) as point
db2022(# from takes, grade_point, course
db2022(# where trim(takes.grade) = grade_point.grade and takes.course_id = course.course_id
db2022(# group by id, takes.course_id, course.credits
db2022(# ) group by id, name;
CREATE VIEW
db2022=# select * from dept_grades;
id | name | dept_name | gpa
-------+--------------------+-------------+-----
92776 | Oki | Psychology | 2.4
94894 | Kozlov | Accounting | 2.7
26881 | Markin | History | 3.0
...
23. 按院系查询学生的GPA排名
db2022=# select ID, dept_name, gpa,
db2022-# rank () over (partition by dept_name order by GPA desc) as dept_rank
db2022-# from dept_grades
db2022-# order by dept_name, dept_rank
db2022-# limit 3;
id | dept_name | gpa | dept_rank
-------+------------+-----+-----------
69679 | Accounting | 3.8 | 1
14829 | Accounting | 3.5 | 2
23457 | Accounting | 3.5 | 2
(3 rows)
24. 创建视图 tot_credits
统计所有学生每年所获得的总学分。
db2022=# create view tot_credits(year,credits) as
db2022-# with temp1(year, credits) as
db2022-# (select year, sum(credits) from takes natural join course
db2022(# where grade <> 'F' or grade is not null
db2022(# group by year),
db2022-# temp2(year, credits) as
db2022-# (select 2001,0 union select 2002,0 union
db2022(# select 2003,0 union select 2004,0 union
db2022(# select 2005,0 union select 2006,0 union
db2022(# select 2007,0 union select 2008,0 union
db2022(# select 2009,0 union select 2010,0 union
db2022(# select 2011,0 union select 2012,0 union
db2022(# select 2013,0 union select 2014,0 union
db2022(# select 2015,0 union select 2016,0 union
db2022(# select 2017,0 union select 2018,0 union
db2022(# select 2019,0 union select 2020,0 union
db2022(# select 2021,0 union select 2022,0)
db2022-# select * from temp1
db2022-# UNION
db2022-# select * from temp2 where year not in (select year from temp1);
CREATE VIEW
db2022=# select * from tot_credits limit 3;
year | credits
------+---------
2012 | 0
2004 | 7085
2018 | 0
(3 rows)
25. 使用窗口函数查询不同条件下所有学生所获得的平均学分
db2022=# select year, round(avg(credits) over (order by year rows 3 preceding), 2)
db2022-# as avg_total_credits
db2022-# from tot_credits
db2022-# limit 3;
year | avg_total_credits
------+-------------------
2001 | 4530.00
2002 | 8984.00
2003 | 10307.00
(3 rows)
db2022=# select year, round(avg(credits) over (order by year rows unbounded preceding), 2)
db2022-# as avg_total_credits
db2022-# from tot_credits
db2022-# limit 3;
year | avg_total_credits
------+-------------------
2001 | 4530.00
2002 | 8984.00
2003 | 10307.00
(3 rows)
db2022=# select year, round(avg(credits) over (order by year rows between 3 preceding and 2 following), 2)
db2022-# as avg_total_credits
db2022-# from tot_credits
db2022-# limit 3;
year | avg_total_credits
------+-------------------
2001 | 10307.00
2002 | 9501.50
2003 | 9362.20
(3 rows)
26. 创建视图 student_tot_credits(id, year, credits)
统计每个学生每年所获得的总学分。
db2022=# create view student_tot_credits as
db2022-# select ID, year, sum(course.credits) as credits
db2022-# from takes natural join course
db2022-# group by ID, year;
CREATE VIEW
db2022=# select * from student_tot_credits limit 3;
id | year | credits
-------+------+---------
108 | 2006 | 4
58874 | 2007 | 4
22618 | 2002 | 3
(3 rows)
27. 查询每个学生连续三年所获得的平均学分数
select ID, year, round(avg(credits)
over (partition by ID order by year rows 3 preceding), 2)
from student_tot_credits;
db2022=# select * from student_tot_credits where id='108';
id | year | credits
-----+------+---------
108 | 2006 | 4
108 | 2010 | 7
108 | 2004 | 4
108 | 2001 | 3
108 | 2008 | 11
108 | 2002 | 10
108 | 2007 | 10
108 | 2003 | 7
(8 rows)
db2022=# select ID, year, round(avg(credits)
db2022(# over (partition by ID order by year rows 3 preceding), 2)
db2022-# from student_tot_credits where id='108';
id | year | round
-----+------+-------
108 | 2001 | 3.00
108 | 2002 | 6.50
108 | 2003 | 6.67
108 | 2004 | 6.00
108 | 2006 | 6.25
108 | 2007 | 6.25
108 | 2008 | 7.25
108 | 2010 | 8.00
(8 rows)