@SovietPower
2022-05-06T08:58:11.000000Z
字数 18166
阅读 2100
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 $$declarebeginend;$$ LANGUAGE [lang];
创建过程。
注意最后还需要一个换行/结束语句。
create or replace procedure public.proc (x in varchar(20), y out integer) asbeginselect count(*) into d_count from instructorwhere instructor.dept_name = d_name;end;/
运行创建的容器。
PS H:\> docker start opengaussPS H:\> docker exec -it opengauss bashroot@97375c3acbd1:/# su ommomm@97375c3acbd1:/$ gsql -d db2022 -rgsql ((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 integerdb2022-# LANGUAGE plpgsqldb2022-# as $$db2022$# declaredb2022$# res integer;db2022$# begindb2022$# 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, budgetdb2022-# from departmentdb2022-# where dept_count(dept_name)>4;dept_name | budget------------+-----------Athletics | 734550.70Statistics | 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 instructordb2022$# 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) asdb2022$# begindb2022$# select count(*) into d_count from instructordb2022$# 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)) asdeclareenrollment integer;lim integer;beginselect count(*) into enrollment from takeswhere 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 sectionwhere course_id = s_course_id and sec_id = s_sec_id and semester=s_semester and year=s_year;if (enrollment < lim) thenbegininsert into takes values(s_id, s_course_id, s_semester, s_year, null);msg := 'Successful!';end;elsemsg := '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(# ) asdb2022$# declaredb2022$# enrollment integer;db2022$# lim integer;db2022$# begindb2022$# select count(*) into enrollment from takesdb2022$# 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 sectiondb2022$# where course_id = s_course_id and sec_id = s_sec_id and semester=s_semester and year=s_year;db2022$# if (enrollment < lim) thendb2022$# begindb2022$# insert into takes values(s_id, s_course_id, s_semester, s_year, null);db2022$# msg := 'Successful!';db2022$# end;db2022$# elsedb2022$# 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)) asdeclaredept varchar(20);cred numeric(2,0);begincreate 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 takeswhere dept_name=dept and course_id=cid);insert into takesselect ID, cid, sec, sem, y, nullfrom stu;update studentset tot_cred = tot_cred+credwhere 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)) asdb2022$# declaredb2022$# dept varchar(20);db2022$# cred numeric(2,0);db2022$# begindb2022$# 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 studb2022$# (select ID from student where dept_name=dept)db2022$# exceptdb2022$# (select ID from student natural join takesdb2022$# where dept_name=dept and course_id=cid);db2022$# insert into takesdb2022$# select ID, cid, sec, sem, y, nulldb2022$# from stu;db2022$# update studentdb2022$# set tot_cred = tot_cred+creddb2022$# where student.ID in (select ID from stu);db2022$# end;db2022$# /CREATE PROCEDUREdb2022=# 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 triggernot fenced not shippable as $$declarebeginif new.time_slot_id not in (select time_slot_id from time_slot) thendelete from sectionwhere 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 triggernot fenced not shippable as $$declarebeginif 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) theninsert 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 triggerdb2022-# not fenced not shippable as $$db2022$# declaredb2022$#db2022$# begindb2022$# if new.time_slot_id not in (select time_slot_id from time_slot) thendb2022$# delete from sectiondb2022$# 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 FUNCTIONdb2022=# create or replace function public.tri_delete_timeslot_func ()db2022-# returns triggerdb2022-# not fenced not shippable as $$db2022$# declaredb2022$#db2022$# begindb2022$# 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) thendb2022$# 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 sectiondb2022-# for each rowdb2022-# execute procedure tri_insert_section_func();CREATE TRIGGERdb2022=# create trigger delete_timeslot_check after delete on time_slotdb2022-# for each rowdb2022-# 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 1db2022=# insert into section values('747','1','Fall',2021,'Gates','314','Q');INSERT 0 1db2022=# 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 1db2022=# 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 | 30Q | F | 10 | 0 | 12 | 30(2 rows)db2022=# delete from time_slot where time_slot_id = 'Q' and day = 'W';DELETE 1db2022=# 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 1db2022=# delete from time_slot where time_slot_id = 'Q' and day = 'F';DELETE 1db2022=# 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 TRIGGERdb2022=# DROP TRIGGER insert_section_timeslot_check on section;DROP TRIGGERdb2022=# DROP FUNCTION IF EXISTS tri_insert_section_func;DROP FUNCTIONdb2022=# DROP FUNCTION IF EXISTS tri_delete_timeslot_func;DROP FUNCTION
18. 创建触发器 tri_update_credits
用于维护学生所获得的学分。
create or replace function public.on_update_credits_func ()returns triggernot fenced not shippable as $$beginif new.grade<>'F' and new.grade is not null and (old.grade='F' or old.grade is null) thenupdate studentset 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 takesfor each rowexecute procedure on_update_credits_func();
db2022=# create or replace function public.on_update_credits_func ()db2022-# returns triggerdb2022-# not fenced not shippable as $$db2022$# begindb2022$# if new.grade<>'F' and new.grade is not null and (old.grade='F' or old.grade is null) thendb2022$# update studentdb2022$# 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 FUNCTIONdb2022=# create trigger on_update_credits after update on takesdb2022-# for each rowdb2022-# execute procedure on_update_credits_func();CREATE TRIGGERdb2022=# select * from takes where grade='F';id | course_id | sec_id | semester | year | grade-------+-----------+--------+----------+------+-------79446 | 808 | 1 | Fall | 2003 | F99710 | 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 1db2022=# 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 asdb2022-# with recursive rec_prereq(course_id, prereq_id) as (db2022(# select course_id, prereq_iddb2022(# from prereqdb2022(# uniondb2022(# select rec_prereq.course_id, prereq.prereq_iddb2022(# from rec_prereq, prereqdb2022(# where rec_prereq.prereq_id = prereq.course_iddb2022(# )db2022-# select * from rec_prereq;CREATE VIEWdb2022=# select * from rec_prereq where course_id='760';course_id | prereq_id-----------+-----------760 | 169760 | 603760 | 735(3 rows)
20. 创建视图 students_gpa
显示学生的GPA。
db2022=# create view students_gpa asdb2022-# select id, name, round(sum(point*credits)/sum(credits), 1) as GPAdb2022-# from student natural left outer join (db2022(# select id, takes.course_id, course.credits, max(point) as pointdb2022(# from takes, grade_point, coursedb2022(# where trim(takes.grade) = grade_point.grade and takes.course_id = course.course_iddb2022(# group by id, takes.course_id, course.creditsdb2022(# ) group by id, name;CREATE VIEWdb2022=# select * from students_gpa where name='Cox';id | name | gpa-------+------+-----59908 | Cox | 2.821766 | Cox | 3.044304 | Cox | 3.4(3 rows)
21. 查询学生的GPA排名
db2022=# select ID, name, gpa, rank() over (order by (GPA) desc) as s_rankdb2022-# from students_gpadb2022-# limit 5;id | name | gpa | s_rank-------+---------+-----+--------79446 | Frost | | 156499 | Zarpell | 3.9 | 281896 | Feldman | 3.9 | 218286 | Pang | 3.9 | 298619 | Nagaraj | 3.9 | 2(5 rows)db2022=# select ID, name, rank() over (order by GPA desc nulls last) as s_rankdb2022-# from students_gpadb2022-# limit 5;id | name | s_rank-------+---------+--------98619 | Nagaraj | 118286 | Pang | 156499 | Zarpell | 181896 | Feldman | 14248 | Wright | 5(5 rows)db2022=# select ID, (1 + (select count(*) from students_gpa B where B.GPA > A.GPA)) as s_rankdb2022-# from students_gpa Adb2022-# order by s_rankdb2022-# limit 5;id | s_rank-------+--------81896 | 179446 | 118286 | 198619 | 156499 | 1(5 rows)
22. 创建视图 dept_grades
显示学生的GPA,包含其院系。
db2022=# create view dept_grades asdb2022-# select id, name, dept_name, round(sum(point*credits)/sum(credits), 1) as GPAdb2022-# from student natural left outer join (db2022(# select id, takes.course_id, course.credits, max(point) as pointdb2022(# from takes, grade_point, coursedb2022(# where trim(takes.grade) = grade_point.grade and takes.course_id = course.course_iddb2022(# group by id, takes.course_id, course.creditsdb2022(# ) group by id, name;CREATE VIEWdb2022=# select * from dept_grades;id | name | dept_name | gpa-------+--------------------+-------------+-----92776 | Oki | Psychology | 2.494894 | Kozlov | Accounting | 2.726881 | 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_rankdb2022-# from dept_gradesdb2022-# order by dept_name, dept_rankdb2022-# limit 3;id | dept_name | gpa | dept_rank-------+------------+-----+-----------69679 | Accounting | 3.8 | 114829 | Accounting | 3.5 | 223457 | Accounting | 3.5 | 2(3 rows)
24. 创建视图 tot_credits
统计所有学生每年所获得的总学分。
db2022=# create view tot_credits(year,credits) asdb2022-# with temp1(year, credits) asdb2022-# (select year, sum(credits) from takes natural join coursedb2022(# where grade <> 'F' or grade is not nulldb2022(# group by year),db2022-# temp2(year, credits) asdb2022-# (select 2001,0 union select 2002,0 uniondb2022(# select 2003,0 union select 2004,0 uniondb2022(# select 2005,0 union select 2006,0 uniondb2022(# select 2007,0 union select 2008,0 uniondb2022(# select 2009,0 union select 2010,0 uniondb2022(# select 2011,0 union select 2012,0 uniondb2022(# select 2013,0 union select 2014,0 uniondb2022(# select 2015,0 union select 2016,0 uniondb2022(# select 2017,0 union select 2018,0 uniondb2022(# select 2019,0 union select 2020,0 uniondb2022(# select 2021,0 union select 2022,0)db2022-# select * from temp1db2022-# UNIONdb2022-# select * from temp2 where year not in (select year from temp1);CREATE VIEWdb2022=# select * from tot_credits limit 3;year | credits------+---------2012 | 02004 | 70852018 | 0(3 rows)
25. 使用窗口函数查询不同条件下所有学生所获得的平均学分
db2022=# select year, round(avg(credits) over (order by year rows 3 preceding), 2)db2022-# as avg_total_creditsdb2022-# from tot_creditsdb2022-# limit 3;year | avg_total_credits------+-------------------2001 | 4530.002002 | 8984.002003 | 10307.00(3 rows)db2022=# select year, round(avg(credits) over (order by year rows unbounded preceding), 2)db2022-# as avg_total_creditsdb2022-# from tot_creditsdb2022-# limit 3;year | avg_total_credits------+-------------------2001 | 4530.002002 | 8984.002003 | 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_creditsdb2022-# from tot_creditsdb2022-# limit 3;year | avg_total_credits------+-------------------2001 | 10307.002002 | 9501.502003 | 9362.20(3 rows)
26. 创建视图 student_tot_credits(id, year, credits)
统计每个学生每年所获得的总学分。
db2022=# create view student_tot_credits asdb2022-# select ID, year, sum(course.credits) as creditsdb2022-# from takes natural join coursedb2022-# group by ID, year;CREATE VIEWdb2022=# select * from student_tot_credits limit 3;id | year | credits-------+------+---------108 | 2006 | 458874 | 2007 | 422618 | 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 | 4108 | 2010 | 7108 | 2004 | 4108 | 2001 | 3108 | 2008 | 11108 | 2002 | 10108 | 2007 | 10108 | 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.00108 | 2002 | 6.50108 | 2003 | 6.67108 | 2004 | 6.00108 | 2006 | 6.25108 | 2007 | 6.25108 | 2008 | 7.25108 | 2010 | 8.00(8 rows)