oracle函数中的多游标
Multi cursor in oracle function
我想创建一个 oracle 函数,它获取 user_id 作为参数和 return varchar2,其中包含用户从图书馆借阅但未借阅的书籍的电子邮件文本return 他们回来了,我想要的电子邮件文本是:
“你好 ”
你应该 return 这本书:
1 在
2. ....
这是我到目前为止写的
create or replace function get_un_recived_books(param_client_id in number) return varchar2 is
Result varchar2(2000);
cursor cur_book is
select * from hashala natural join client natural join all_books natural join book
where
recived =0
and recived_date is null
and clientid= param_client_id
and taken_date < add_months(trunc(sysdate, 'month'), -3);
begin
FOR b IN cur_book LOOP
Result:= 'book ' || b.book_name;
END LOOP;
return(Result);
end;
我有 3 个问题
我怎么能 return 所有的书而不是最后一本书(就像 c 中的 +=)
客户端名称需要再添加一个游标吗?
如何传递原始查询的值
select get_un_recived_books(!!!ADD HERE THE CLIENID!!!), clientid from hashala natural join client natural join all_books natural join
本书
在哪里
收到=0
recived_date 为空
and taken_date < add_months(trunc(sysdate, 'month'), -3);
我没有您的表格,因此我将使用 Scott 的示例架构来说明如何您可以这样做。阅读代码中的注释。
函数:
SQL> create or replace function f_test (par_deptno in number)
2 return varchar2
3 is
4 l_dname dept.dname%type;
5 retval varchar2(4000);
6 begin
7 -- department name (in your case, client name)
8 select dname
9 into l_dname
10 from dept
11 where deptno = par_deptno;
12
13 -- loop through employees in PAR_DEPTNO (in your case,
14 -- books client borrowed)
15 for cur_r in (select ename, hiredate
16 from emp
17 where deptno = par_deptno
18 )
19 loop
20 -- this is what you're missing: "retval := retval || ..."
21 retval := retval || 'Name: ' || cur_r.ename ||
22 ', borrowed on ' || to_char(cur_r.hiredate, 'dd.mm.yyyy') || chr(10);
23 end loop;
24
25 retval := 'Hello, ' || l_dname ||chr(10) ||
26 'you borrowed the following books and didn''t return them yet.' || chr(10) ||
27 retval;
28 return retval;
29 end;
30 /
Function created.
测试:
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------------------------------------------------------------------
Hello, ACCOUNTING
you borrowed the following books and didn't return them yet.
Name: CLARK, borrowed on 09.06.1981
Name: KING, borrowed on 17.11.1981
Name: MILLER, borrowed on 23.01.1982
SQL>
如果您 - 如您所说 - 想要“动态”传递 ID,只需将其包含到函数中即可。类似这样的事情(我正在检索部门 10 和部门 30 的数据,适用于所有担任文员的人):
SQL> select f_test(d.deptno)
2 from dept d join emp e on e.deptno = d.deptno
3 where d.deptno in (10, 30)
4 and e.job = 'CLERK';
F_TEST(D.DEPTNO)
------------------------------------------------------------------------
Hello, SALES
you borrowed the following books and didn't return them yet.
Name: ALLEN, borrowed on 20.02.1981
Name: WARD, borrowed on 22.02.1981
Name: MARTIN, borrowed on 28.09.1981
Name: BLAKE, borrowed on 01.05.1981
Name: TURNER, borrowed on 08.09.1981
Name: JAMES, borrowed on 03.12.1981
Hello, ACCOUNTING
you borrowed the following books and didn't return them yet.
Name: CLARK, borrowed on 09.06.1981
Name: KING, borrowed on 17.11.1981
Name: MILLER, borrowed on 23.01.1982
SQL>
您可以使用LISTAGG
function to achieve this. This task can be solved in pure SQL via view,与函数调用相比,它不会产生子游标。如果您希望 select 个人客户,您只需要对其进行过滤。
但是,如果您愿意,您也可以将相同的文本放入一个函数中,这也不需要任何循环。因为从 Oracle 12C 及更高版本开始,您可以使用 MAX_STRING_SIZE = EXTENDED
参数将 varchar2
扩展到最多 32767 字节,这与 PL/SQL 相比,删除了 SQL 中 varchar2
的边界。
两个例子如下:
create table client
as
select
level as id
, 'Client ' || level as name
from dual
connect by level < 10
create table book
as
select
level as id
, 'Book ' || level as name
from dual
connect by level < 100
create table borrowed_book
as
select
trunc(level / 4) + 1 as client_id
, level as book_id
, add_months(sysdate, -mod(level, 11)) as dt
, case mod(level, 4) when 1 then 1 else 0 end as received
from dual
connect by level < 60
create view v_reminder
as
select
bb.client_id,
c.name as client_name,
'Hello, ' || max(c.name) || '!'
|| chr(10) || chr(10)
|| 'You should return this books:' || chr(10)
|| listagg(b.name || ' borrowed on ' || to_char(bb.dt, 'dd-Mon-yyyy'), chr(10))
within group(order by bb.dt desc, bb.book_id) as rem
from borrowed_book bb
join client c
on bb.client_id = c.id
join book b
on bb.book_id = b.id
where bb.received = 0
and bb.dt < add_months(sysdate, -3)
group by bb.client_id, c.name
create or replace function f_get_reminder(p_client_id in int, p_dt_offset in int default 3)
return varchar2
as
res varchar2(32000);
begin
select
'Hello, ' || max(c.name) || '!'
|| chr(10) || chr(10)
|| 'You should return this books:' || chr(10)
|| listagg(b.name || ' borrowed on ' || to_char(bb.dt, 'dd-Mon-yyyy'), chr(10))
within group(order by bb.dt desc, bb.book_id)
into res
from borrowed_book bb
join client c
on bb.client_id = c.id
join book b
on bb.book_id = b.id
where bb.received = 0
and bb.dt < add_months(sysdate, -p_dt_offset)
and bb.client_id = p_client_id
/*Added this line to get NULL as output, because aggregate function
without group by always returns a row even for empty input dataset*/
group by bb.client_id
;
return res;
end;
/
select
c.id
, c.name
, f_get_reminder(c.id) as rem
from client c
ID
NAME
REM
1
Client 1
null
2
Client 2
Hello, Client 2!
You should return this books:
Book 4 borrowed on 27-Feb-2021
Book 6 borrowed on 27-Dec-2020
Book 7 borrowed on 27-Nov-2020
3
Client 3
Hello, Client 3!
You should return this books:
Book 8 borrowed on 27-Oct-2020
Book 10 borrowed on 27-Aug-2020
4
Client 4
Hello, Client 4!
You should return this books:
Book 15 borrowed on 27-Feb-2021
5
Client 5
Hello, Client 5!
You should return this books:
Book 16 borrowed on 27-Jan-2021
Book 18 borrowed on 27-Nov-2020
Book 19 borrowed on 27-Oct-2020
6
Client 6
Hello, Client 6!
You should return this books:
Book 20 borrowed on 27-Sep-2020
7
Client 7
Hello, Client 7!
You should return this books:
Book 26 borrowed on 27-Feb-2021
Book 27 borrowed on 27-Jan-2021
8
Client 8
Hello, Client 8!
You should return this books:
Book 28 borrowed on 27-Dec-2020
Book 30 borrowed on 27-Oct-2020
Book 31 borrowed on 27-Sep-2020
9
Client 9
Hello, Client 9!
You should return this books:
Book 32 borrowed on 27-Aug-2020
db<>fiddle here
我想创建一个 oracle 函数,它获取 user_id 作为参数和 return varchar2,其中包含用户从图书馆借阅但未借阅的书籍的电子邮件文本return 他们回来了,我想要的电子邮件文本是:
“你好 ”
你应该 return 这本书:
1
这是我到目前为止写的
create or replace function get_un_recived_books(param_client_id in number) return varchar2 is
Result varchar2(2000);
cursor cur_book is
select * from hashala natural join client natural join all_books natural join book
where
recived =0
and recived_date is null
and clientid= param_client_id
and taken_date < add_months(trunc(sysdate, 'month'), -3);
begin
FOR b IN cur_book LOOP
Result:= 'book ' || b.book_name;
END LOOP;
return(Result);
end;
我有 3 个问题
我怎么能 return 所有的书而不是最后一本书(就像 c 中的 +=)
客户端名称需要再添加一个游标吗?
如何传递原始查询的值
select get_un_recived_books(!!!ADD HERE THE CLIENID!!!), clientid from hashala natural join client natural join all_books natural join
本书 在哪里 收到=0 recived_date 为空
and taken_date < add_months(trunc(sysdate, 'month'), -3);
我没有您的表格,因此我将使用 Scott 的示例架构来说明如何您可以这样做。阅读代码中的注释。
函数:
SQL> create or replace function f_test (par_deptno in number)
2 return varchar2
3 is
4 l_dname dept.dname%type;
5 retval varchar2(4000);
6 begin
7 -- department name (in your case, client name)
8 select dname
9 into l_dname
10 from dept
11 where deptno = par_deptno;
12
13 -- loop through employees in PAR_DEPTNO (in your case,
14 -- books client borrowed)
15 for cur_r in (select ename, hiredate
16 from emp
17 where deptno = par_deptno
18 )
19 loop
20 -- this is what you're missing: "retval := retval || ..."
21 retval := retval || 'Name: ' || cur_r.ename ||
22 ', borrowed on ' || to_char(cur_r.hiredate, 'dd.mm.yyyy') || chr(10);
23 end loop;
24
25 retval := 'Hello, ' || l_dname ||chr(10) ||
26 'you borrowed the following books and didn''t return them yet.' || chr(10) ||
27 retval;
28 return retval;
29 end;
30 /
Function created.
测试:
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------------------------------------------------------------------
Hello, ACCOUNTING
you borrowed the following books and didn't return them yet.
Name: CLARK, borrowed on 09.06.1981
Name: KING, borrowed on 17.11.1981
Name: MILLER, borrowed on 23.01.1982
SQL>
如果您 - 如您所说 - 想要“动态”传递 ID,只需将其包含到函数中即可。类似这样的事情(我正在检索部门 10 和部门 30 的数据,适用于所有担任文员的人):
SQL> select f_test(d.deptno)
2 from dept d join emp e on e.deptno = d.deptno
3 where d.deptno in (10, 30)
4 and e.job = 'CLERK';
F_TEST(D.DEPTNO)
------------------------------------------------------------------------
Hello, SALES
you borrowed the following books and didn't return them yet.
Name: ALLEN, borrowed on 20.02.1981
Name: WARD, borrowed on 22.02.1981
Name: MARTIN, borrowed on 28.09.1981
Name: BLAKE, borrowed on 01.05.1981
Name: TURNER, borrowed on 08.09.1981
Name: JAMES, borrowed on 03.12.1981
Hello, ACCOUNTING
you borrowed the following books and didn't return them yet.
Name: CLARK, borrowed on 09.06.1981
Name: KING, borrowed on 17.11.1981
Name: MILLER, borrowed on 23.01.1982
SQL>
您可以使用LISTAGG
function to achieve this. This task can be solved in pure SQL via view,与函数调用相比,它不会产生子游标。如果您希望 select 个人客户,您只需要对其进行过滤。
但是,如果您愿意,您也可以将相同的文本放入一个函数中,这也不需要任何循环。因为从 Oracle 12C 及更高版本开始,您可以使用 MAX_STRING_SIZE = EXTENDED
参数将 varchar2
扩展到最多 32767 字节,这与 PL/SQL 相比,删除了 SQL 中 varchar2
的边界。
两个例子如下:
create table client as select level as id , 'Client ' || level as name from dual connect by level < 10
create table book as select level as id , 'Book ' || level as name from dual connect by level < 100
create table borrowed_book as select trunc(level / 4) + 1 as client_id , level as book_id , add_months(sysdate, -mod(level, 11)) as dt , case mod(level, 4) when 1 then 1 else 0 end as received from dual connect by level < 60
create view v_reminder as select bb.client_id, c.name as client_name, 'Hello, ' || max(c.name) || '!' || chr(10) || chr(10) || 'You should return this books:' || chr(10) || listagg(b.name || ' borrowed on ' || to_char(bb.dt, 'dd-Mon-yyyy'), chr(10)) within group(order by bb.dt desc, bb.book_id) as rem from borrowed_book bb join client c on bb.client_id = c.id join book b on bb.book_id = b.id where bb.received = 0 and bb.dt < add_months(sysdate, -3) group by bb.client_id, c.name
create or replace function f_get_reminder(p_client_id in int, p_dt_offset in int default 3) return varchar2 as res varchar2(32000); begin select 'Hello, ' || max(c.name) || '!' || chr(10) || chr(10) || 'You should return this books:' || chr(10) || listagg(b.name || ' borrowed on ' || to_char(bb.dt, 'dd-Mon-yyyy'), chr(10)) within group(order by bb.dt desc, bb.book_id) into res from borrowed_book bb join client c on bb.client_id = c.id join book b on bb.book_id = b.id where bb.received = 0 and bb.dt < add_months(sysdate, -p_dt_offset) and bb.client_id = p_client_id /*Added this line to get NULL as output, because aggregate function without group by always returns a row even for empty input dataset*/ group by bb.client_id ; return res; end; /
select c.id , c.name , f_get_reminder(c.id) as rem from client c
ID NAME REM 1 Client 1 null 2 Client 2 Hello, Client 2!
You should return this books:
Book 4 borrowed on 27-Feb-2021
Book 6 borrowed on 27-Dec-2020
Book 7 borrowed on 27-Nov-20203 Client 3 Hello, Client 3!
You should return this books:
Book 8 borrowed on 27-Oct-2020
Book 10 borrowed on 27-Aug-20204 Client 4 Hello, Client 4!
You should return this books:
Book 15 borrowed on 27-Feb-20215 Client 5 Hello, Client 5!
You should return this books:
Book 16 borrowed on 27-Jan-2021
Book 18 borrowed on 27-Nov-2020
Book 19 borrowed on 27-Oct-20206 Client 6 Hello, Client 6!
You should return this books:
Book 20 borrowed on 27-Sep-20207 Client 7 Hello, Client 7!
You should return this books:
Book 26 borrowed on 27-Feb-2021
Book 27 borrowed on 27-Jan-20218 Client 8 Hello, Client 8!
You should return this books:
Book 28 borrowed on 27-Dec-2020
Book 30 borrowed on 27-Oct-2020
Book 31 borrowed on 27-Sep-20209 Client 9 Hello, Client 9!
You should return this books:
Book 32 borrowed on 27-Aug-2020
db<>fiddle here