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 个问题

  1. 我怎么能 return 所有的书而不是最后一本书(就像 c 中的 +=)

  2. 客户端名称需要再添加一个游标吗?

  3. 如何传递原始查询的值

      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