【问题标题】:Multi cursor in oracle functionoracle函数中的多光标
【发布时间】:2021-09-09 22:37:52
【问题描述】:

我想创建一个 oracle 函数,将 user_id 作为参数并返回 varchar2,其中包含用户从图书馆拿走但没有返回的书籍的电子邮件文本,我想要的电子邮件文本是: “你好 ” 你应该归还这本书: 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. 我怎样才能退回所有的书,而不是最后一本书(比如 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);
    

【问题讨论】:

    标签: oracle cursor dbfunctions


    【解决方案1】:

    我没有你的表格,所以我将使用 Scott 的示例模式来说明如何你可以这样做。在代码中读取 cmets。

    功能:

    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>
    

    【讨论】:

    • 不客气。我添加了更多代码来回答这个问题。请看一下。
    【解决方案2】:

    您可以使用LISTAGG 函数来实现这一点。这个任务可以通过view在纯SQL中解决,与函数调用相比,它不会产生子游标。如果您想选择个别客户,您只需要过滤它。

    但是,如果您愿意,您也可以将相同的文本放入函数中,这也不需要任何循环。因为从 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小提琴here

    【讨论】:

      猜你喜欢
      • 2020-02-26
      • 2017-11-23
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 2013-08-18
      • 2010-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多