【问题标题】:How to create Oracle stored procedure which can return specific entities as well all entity如何创建可以返回特定实体以及所有实体的 Oracle 存储过程
【发布时间】:2011-05-12 20:06:21
【问题描述】:

我是存储过程的初学者。

我们需要创建一个存储过程,它应该返回所有记录或只返回作为IN参数传递的,过程返回游标。

如果通过传递accountId 列表调用过程,它应该只返回那些帐户,否则返回所有帐户。

如果你能举个例子那就太好了。

【问题讨论】:

    标签: oracle stored-procedures plsql


    【解决方案1】:

    这是一个简单的例子:

    考虑表格:PERSONS (person_id, name)

    如果没有提供参数,此函数将返回一个游标,该游标返回一条记录或所有记录:

    CREATE FUNCTION get_person
       (person_id IN persons.person_id%TYPE := NULL)
       RETURN SYS_REFCURSOR IS
       rc SYS_REFCURSOR;
    BEGIN
       OPEN rc FOR
         SELECT *
         FROM   persons p
         WHERE  p.person_id = get_person.person_id
         OR     get_person.person_id IS NULL;
       RETURN rc;
    END;
    

    【讨论】:

      【解决方案2】:

      我们从一个嵌套表类型开始,我们可以使用它来传递一个数字列表。这需要是 SQL 类型,因为我们将在 select 语句中使用它。

      create or replace type numbers_nt as table of number
      /
      

      这是一个以数字集合为参数的函数。如果集合被填充,它将使用这些数字来限制对 EMP 表的查询,否则它会选择所有记录。这使用动态 SQL 返回一个引用游标结果集。

      create or replace function qry_emps
              (p_nt in numbers_nt)
              return sys_refcursor
      as
          rv sys_refcursor;
          stmt varchar2(32767) := 'select * from emp';
      begin
          if p_nt.count() > 0
          then
              stmt := stmt || ' where empno in ( select * from table(:1) )';
              open rv for stmt
                  using p_nt;
          else
              open rv for stmt;
          end if;
          return rv;
      end qry_emps;
      /
      

      所以,首先我们传递一个填充的集合:

      SQL> set serveroutput on
      SQL>
      SQL> declare
        2      empty_nt numbers_nt := new numbers_nt();
        3      pop_nt numbers_nt := new numbers_nt(7876,8083,7788);
        4      rc sys_refcursor;
        5      lrec emp%rowtype;
        6  begin
        7      rc := qry_emps(pop_nt);
        8      dbms_output.put_line ( 'Three rows');
        9
       10      loop
       11          fetch rc into lrec;
       12          exit when rc%notfound;
       13          dbms_output.put_line('empno = '||lrec.empno);
       14      end loop;
       15
       16      dbms_output.put_line ( 'Done');
       17  end;
       18  /
      Three rows
      empno = 7876
      empno = 8083
      empno = 7788
      Done
      
      PL/SQL procedure successfully completed.
      
      SQL>
      

      现在我们编辑匿名块以传递一个空集合:

      SQL> declare
        2      empty_nt numbers_nt := new numbers_nt();
        3      rc sys_refcursor;
        4      lrec emp%rowtype;
        5  begin
        6      rc := qry_emps(empty_nt);
        7      dbms_output.put_line ( 'all rows');
        8
        9      loop
       10          fetch rc into lrec;
       11          exit when rc%notfound;
       12          dbms_output.put_line('empno = '||lrec.empno);
       13      end loop;
       14
       15      dbms_output.put_line ( 'Done');
       16  end;
       17  /
      all rows
      empno = 8083
      empno = 8084
      empno = 8085
      empno = 7369
      empno = 7499
      empno = 7521
      empno = 7566
      empno = 7654
      empno = 7698
      empno = 7782
      empno = 7788
      empno = 7839
      empno = 7844
      empno = 7876
      empno = 7900
      empno = 7902
      empno = 7934
      empno = 8060
      empno = 8061
      empno = 8100
      empno = 8101
      Done
      
      PL/SQL procedure successfully completed.
      
      SQL>
      

      【讨论】:

        【解决方案3】:

        以下使用PIPELINED 函数返回行。流水线函数的好处是它们返回与函数终止异步的行(您立即开始获取行,而不是在结尾处获取所有行)。它们也可以针对并行查询进行优化。如此明确的性能优势。

        另外,返回游标是强类型的(不像 sys_refcursor 那样弱,当底层表发生变化时可以看到运行时异常等)。

        set echo on
        set serveroutput on
        
        drop table people;
        create table people
        (
        pid number primary key,
        name varchar2(100),
        address varchar2(100),
        city varchar2(100),
        state varchar2(2)
        );
        
        insert into people values (1, 'John Smith', '123 Main St', 'Denver', 'CO');
        insert into people values (2, 'Jane Doe', '456 West St', 'Ft Lauderdale', 'FL');
        insert into people values (3, 'Pete Rose', '789 North Ave', 'Philadelphia', 'PA');
        commit;
        

        创建类型:

        create or replace package refcur_pkg is
            type people_tab is table of people%rowtype;
        end refcur_pkg;
        
        create or replace type pid_tab as table of number;
        

        还有main函数(放什么业务逻辑在这里)

        -- pipelined function to return people based on list of people ids
        create or replace function get_people(pids in pid_tab)
        return refcur_pkg.people_tab pipelined
        IS
            v_people_row people%rowtype;
        begin
            --
            -- Note: business rule is no input ids returns ALL rows:
            --
            if (pids is null or pids.count = 0) then
                -- return all rows
                for rec in (select * from people)
                loop
                    pipe row(rec);
                end loop;
            else
                -- return rows based on ids
                for rec in (select * from people where pid in (select * from table(pids)))
                loop
                    pipe row(rec); 
                end loop;
            end if;
        end;
        

        一些使用示例

        -- EXAMPLES
        -- get any/all people with any of these ids
        select * from table(get_people(new pid_tab(1,3,4,5)));
        
        -- gets nobody (nobody with this pid)
        select * from table(get_people(new pid_tab(-1)));
        
        -- get ALL people
        select * from table(get_people(new pid_tab()));
        
        -- also gets ALL people
        select * from table(get_people(NULL));
        

        【讨论】:

          【解决方案4】:

          您可以通过将声明的表类型传递给过程来做到这一点。

          这是我的测试表和数据:

          CREATE TABLE accounts
              ( account_id NUMBER
              , NAME      VARCHAR2(100)
              );
          
          INSERT INTO accounts values ( 1, 'Tom Selleck');
          INSERT INTO accounts VALUES ( 2, 'Elvis Presley');
          INSERT INTO accounts VALUES ( 3, 'Morgan Freeman');
          INSERT INTO accounts values ( 4, 'Harry Morgan');
          
          commit;
          

          现在我创建声明的对象类型和表类型:

          CREATE TYPE accountId_rec AS OBJECT ( account_id NUMBER );
          CREATE TYPE accountid_tbl AS TABLE OF accountid_rec;
          

          接下来是函数:

          CREATE OR REPLACE
          FUNCTION get_accounts ( p_accounts IN accountId_tbl ) 
              RETURN sys_refcursor 
          IS
              retcur sys_refcursor;
          BEGIN
              IF ( p_accounts IS NULL OR p_accounts.count < 1 ) THEN
                  OPEN retcur FOR SELECT * FROM accounts;
              ELSE
                  OPEN retcur FOR SELECT * FROM accounts WHERE account_id IN ( SELECT account_id FROM TABLE( p_accounts ));
              END IF;
              RETURN retcur;
          EXCEPTION
              WHEN OTHERS THEN dbms_output.put_line('get_accounts error: '||sqlerrm);
          END;
          

          现在,用 pl/sql 块测试函数:

          DECLARE
              p_accounts  accountId_tbl := accountId_tbl();
              account_rec accounts%rowtype;
              ref_cur     sys_refcursor;
          BEGIN
              dbms_output.put_line('Test with no Account ID''s.');
              ref_cur := get_accounts( p_accounts );
              LOOP
                  FETCH ref_cur INTO account_rec;
                      EXIT WHEN ref_cur%NOTFOUND;
                  dbms_output.put_line('Account ID: '||account_rec.account_id||', Name: '||account_rec.name);
              END LOOP;
          
              dbms_output.put_line('');
          
              -- now let's test with account ids provided.
              dbms_output.put_line('Test with Account ID''s.');
              p_accounts.EXTEND;
              p_accounts( p_accounts.count ) := accountId_rec(2);
              p_accounts.EXTEND;
              p_accounts( p_accounts.count ) := accountId_rec(4);
              -- get the new ref_cur
              ref_cur := get_accounts( p_accounts );
              LOOP
                  FETCH ref_cur INTO account_rec;
                      EXIT WHEN ref_cur%NOTFOUND;
                  dbms_output.put_line('Account ID: '||account_rec.account_id||', Name: '||account_rec.name);
              END LOOP;
          EXCEPTION
              WHEN OTHERS THEN dbms_output.put_line('whoops: '||sqlerrm);
          END;
          

          以及测试结果:

          Test with no Account ID's.
          Account ID: 1, Name: Tom Selleck
          Account ID: 2, Name: Elvis Presley
          Account ID: 3, Name: Morgan Freeman
          Account ID: 4, Name: Harry Morgan
          
          Test with Account ID's.
          Account ID: 2, Name: Elvis Presley
          Account ID: 4, Name: Harry Morgan
          

          我希望这会有所帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-06-04
            • 1970-01-01
            • 2012-05-07
            • 2014-10-09
            • 1970-01-01
            • 1970-01-01
            • 2012-11-11
            • 2018-09-03
            相关资源
            最近更新 更多