如何创建可以返回特定实体以及所有实体的 Oracle 存储过程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5983821/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 23:39:41  来源:igfitidea点击:

How to create Oracle stored procedure which can return specific entities as well all entity

oraclestored-proceduresplsql

提问by Mat

I am beginner to stored procedure.

我是存储过程的初学者。

We need to create a stored procedure which should return all records or return only passed as IN parameter, procedure returns cursor.

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

If procedure is called by passing list of accountId, it should return only those accounts, else all accounts.

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

If you can place some example it would be great.

如果你能举一些例子,那就太好了。

回答by Jeffrey Kemp

Here's a simple example:

这是一个简单的例子:

Consider the table: PERSONS (person_id, name)

考虑下表: PERSONS (person_id, name)

This function will return a cursor that returns either one record, or all records if no argument is supplied:

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

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;

回答by APC

We start with a nested table type which we can use to pass a list of numbers. This needs to be a SQL type, because we will be using it in a select statement.

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

create or replace type numbers_nt as table of number
/

Here is a function which takes a number collection as a parameter. If the collection is populated it uses those numbers to restrict a query on the EMP table, otherwise it selects all records. This uses dynamic SQL to return a ref cursor result set.

这是一个将数字集合作为参数的函数。如果集合被填充,它使用这些数字来限制对 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;
/

So, first we pass a populated collection:

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

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>

Now we edit the anonymous block to pass an empty collection:

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

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>

回答by tbone

The following uses a PIPELINEDfunction to return the rows. The nice part about pipelined functions is that they return rows asynchronous to the termination of the function (you start getting rows immediately rather than all at the end). They can also be optimized for parallel queries as well. So definite performance benefits.

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

Also, the return cursor is strongly typed (not weak as in sys_refcursor, which can see runtime exceptions when the underlying table changes, etc).

此外,返回游标是强类型的(不像在 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 types:

创建类型:

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;

And the main function (put whatever business logic here)

和主要功能(将任何业务逻辑放在这里)

-- 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;

Some usage examples

一些使用示例

-- 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));

回答by Michael Rickman

You can do this with passing a declared table type into the procedure.

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

Here is my test table and data:

这是我的测试表和数据:

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;

Now I create the declared object type and table type:

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

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

Next is the function:

接下来是函数:

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;

Now, to test the function with a pl/sql block:

现在,使用 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;

And the test results:

以及测试结果:

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

I hope this helps.

我希望这有帮助。