在退出存储过程之前检测空游标
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%rowcount
或 no_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;
/
我有以下声明:
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%rowcount
或 no_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;
/