在退出存储过程之前检测空游标

Detecting empty cursor before exiting stored procedure

我有以下声明:

OPEN PARAM_RS FOR
SELECT *
FROM my_table
WHERE my_NUMBER = param_number;

如何"accurately"测试PARAM_US,以便在它为空时执行另一个语句? 我还需要returnPARAM_US作为OUT参数

我试过了

if (PARAM_RS%rowcount = 0) THEN...

但是即使结果集中有行,这个测试也是肯定的。

我也尝试使用 NO_DATA_FOUND 异常,但它没有被触发。

谢谢!

您必须从游标中获取才能找到那里是否有东西。看看下面的例子:

SQL> declare
  2    param_rs sys_refcursor;
  3    rec      emp%rowtype;
  4  begin
  5    open param_rs for
  6      select * from emp
  7      where deptno = &par_deptno;
  8    fetch param_rs into rec;
  9    if param_rs%notfound then
 10       dbms_output.put_line('Nothing has been found');
 11    else
 12       dbms_output.put_line('Oh yes, I found something!');
 13    end if;
 14  end;
 15  /
Enter value for par_deptno: 10
Oh yes, I found something!

PL/SQL procedure successfully completed.

SQL> /
Enter value for par_deptno: 87
Nothing has been found

PL/SQL procedure successfully completed.

SQL>

[编辑]

如果您想重复使用它以获得整个数据集,是的 - 您必须再次打开它。否则,第一行(您为检查游标中是否有内容而获取的行)将是 "lost".

这是一个例子:

SQL> create or replace procedure p_test (par_deptno in number, param_rs out sys_refcursor)
  2  is
  3    rec      emp%rowtype;
  4  begin
  5    open param_rs for
  6      select * from emp
  7      where deptno = par_deptno;
  8    fetch param_rs into rec;
  9    if param_rs%notfound then
 10       dbms_output.put_line('Nothing has been found');
 11    else
 12       dbms_output.put_line('Oh yes, I found something!');
 13       open param_rs for
 14         select * from emp
 15         where deptno = par_deptno;
 16    end if;
 17  end;
 18  /

Procedure created.

SQL> select ename from emp where deptno = 10;

ENAME
----------
CLARK
KING
MILLER

SQL> var l_out refcursor
SQL> exec p_test(10, :l_out);
Oh yes, I found something!

PL/SQL procedure successfully completed.

SQL> print l_out

     EMPNO ENAME      JOB              MGR HIREDATE          SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ---------- ---------- ---------- ----------
      7782 CLARK      MANAGER         7839 09.06.1981       2450                    10
      7839 KING       PRESIDENT            17.11.1981       5000                    10
      7934 MILLER     CLERK           7782 23.01.1982       1300                    10

SQL>
游标的

%ROWCOUNT 属性在打开时将清零,即使结果集中有行。属性 return 是到目前为止获取的行数,因此在第一个 FETCH 之前它将为零。

类似地,NO_DATA_FOUND 异常不会由 OPEN 引发,当 FETCH 没有 return 行时抛出。


为了回答您的问题,我认为没有任何测试打开的游标来确定其 "empty" ,而不尝试从中获取。

引用游标是指向一块内存的指针:打开它只是将查询关联到内存 space 但不会执行它。直到我们执行提取,我们才知道查询是否会 return 任何东西。这没什么奇怪的,这正是 SQL 查询的正常工作方式。

这就是为什么 sql%rowcountno_data_found 的测试都不起作用的原因:它们是在获取后填充的。如果不实际尝试,就无法 "test" 游标是否会 return 记录。

那么,如何处理没有return任何记录的游标呢?正常行为是允许调用程序处理它。这是通常的实现。通常是因为它通常是调用程序,它拥有有关当前事务的最多信息,因此最适合知道要做什么。

看起来像这样 (full implementation on LiveSQL - free Oracle TechNet account required):

给定一个 API ...

create or replace package test1 as
    type cur1 is ref cursor return my_table%rowtype;

    procedure get_param (p_param in number, p_rs out cur1);
    procedure get_def_param (p_rs out cur1);
end;
/

...调用程序像这样与其交互:

declare
    rc test1.cur1;
    rec my_table%rowtype;
begin
    test1.get_param(42, rc);
    fetch rc into rec;
    if rc%notfound then
        close rc;
        test1.get_def_param(rc);
        fetch rc into rec;
    end if;
    dbms_output.put_line('result='||rec.description);
    close rc;
end;
/

但是,如果你真的想让被调用的程序处理空结果集,这是可以做到的。诀窍是在被调用程序中打开一个测试游标,如果没有获取任何内容,则获取默认记录,然后打开具有保证结果集的参数引用游标。

create or replace package test1 as
    type cur1 is ref cursor return my_table%rowtype;
    type nt is table of my_table%rowtype;

    procedure get_param (p_param in number, p_rs out cur1);
end;
/
create or replace package body test1 as


    procedure get_param (p_param in number, p_rs out cur1) is
        rc cur1;
        recs nt;
    begin
        open rc for
             select t.* 
             from my_table t
             where t.my_number = p_param;
        fetch rc bulk collect into recs;
        if recs.count() = 0 then
            close rc;
            open rc for
                select t.* 
                from my_table t
                where t.my_number = 1;
            fetch rc bulk collect into recs;
        end if;
        close rc;
        open p_rs for 
             select * from table(recs);
    end  get_param;

end;
/

现在调用程序简单多了:

declare
    rc test1.cur1;
    rec my_table%rowtype;
begin
    test1.get_param(42, rc);
    fetch rc into rec;
    dbms_output.put_line('result='||rec.description);
    close rc;
end;
/