这是一个非常基本的问题,但是将FORELSE
写入Oracle PL / SQL中的隐式游标的最佳方法是什么?一些编程语言允许这种语法,当游标没有返回任何行时执行FORELSE
块。
我想要实现的是以下几行(伪代码):
DECLARE
CURSOR test_cur IS
SELECT 'a'
FROM table_with_zero_or_more_data;
BEGIN
FOR r_ IN test_cur LOOP
Dbms_Output.Put_Line ('One extra row found');
FORELSE
Dbms_Output.Put_Line ('No data found');
END LOOP;
END;
我尝试使用如下的异常,但NO_DATA_FOUND
异常不会被隐式游标触发。
DECLARE
CURSOR test_cur IS
SELECT 'a'
FROM table_with_zero_or_more_data;
BEGIN
BEGIN
FOR r_ IN test_cur LOOP
Dbms_Output.Put_Line ('One extra row found');
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
Dbms_Output.Put_Line ('No data found');
END;
END;
以下确实有效(并且有类似的变化,我毫不怀疑)。但我发现这种方式有点笨重。当代码变得更加现实和冗长时,IF
语句与FOR
循环相关联并不那么明显/直观。它还强制引入一个虚拟变量,并且在语法上并不美观。
DECLARE
CURSOR test_cur IS
SELECT 'a'
FROM table_with_zero_or_more_data;
i NUMBER := 0;
BEGIN
FOR r_ IN test_cur LOOP
i := i + 1;
Dbms_Output.Put_Line ('One extra row found');
END LOOP;
IF i = 0 THEN
Dbms_Output.Put_Line ('No data found');
END IF;
END;
我只是想知道是否有更好的方法,更直观并整合了FOR
和IF
条件?
修改
如果我不够清楚,重点是在IMPLICIT游标内部进行。我知道我可以使用EXPlicit游标并检查%NOTFOUND
等(实际上%ROWCOUNT
更好)。
答案 0 :(得分:4)
AFAIK,因为PL / SQL缺少forelse
构造,并且没有办法滚动自己的构造,你可以希望使用额外的状态变量来满足隐式游标for
的情况-loop没有处理任何行。
但是,您可以通过这种方式构建代码,以便更明显地发生了什么:
declare
cursor test_c is
with data_ as (
select 1 as id, 'foo' as str from dual union all
select 2 as id, 'bar' as str from dual
)
select str from data_ where id > 2;
-- isolate the data processing into a dedicated subroutine/package
-- pass all required information as parameters
procedure process_data(p_data in test_c%rowtype) is
begin
dbms_output.put_line('processing: ' || p_data.str);
end;
begin
-- more things can take place here ...
-- isolate the data processing into a dedicated block or subroutine
-- with a block level comment like: processing all foos and bars to
-- conform business rule car. (or even better: name the subroutine
-- accordingly !)
declare
v_has_data boolean := false;
begin
for d in test_c loop
v_has_data := true;
process_data(d);
end loop;
if not v_has_data then
dbms_output.put_line('no data processed');
end if;
end;
-- more things can take place here ...
end;
/
即使我非常同意这种方法的错误...... clunkiness ,请记住所有编程语言都需要权衡。
答案 1 :(得分:1)
尝试使用简单的循环并使用游标的%NOTFOUND
属性进行测试,如下所示:
DECLARE
CURSOR test_cur IS
SELECT 'a'
FROM table_with_zero_or_more_data;
r_test test_cur%ROWTYPE;
BEGIN
OPEN test_cur;
FETCH test_cur INTO r_test;
IF test_cur%NOTFOUND THEN
dbms_output.put_line('No data found');
ELSE
LOOP
FETCH test_cur INTO r_test;
EXIT WHEN test_cur%NOTFOUND;
dbms_output.put_line('One extra row found');
--
-- other routine code if there are rows found.
--
END LOOP;
END IF;
END;
/
有关FETCH
的详细信息,请参阅Oracle Docs。
另见Nicholas Krasnov在下面的评论中提到的SQL Fiddle。这非常有帮助。
答案 2 :(得分:0)
类似的方法是使用游标的%ROWCOUNT属性。有关其他游标属性的参考,请使用链接http://docs.oracle.com/cd/B12037_01/appdev.101/b10807/13_elems011.htm 使用ROWCOUNT的实现如下所示。
DECLARE
CURSOR test_cur IS
SELECT 'a'
FROM table_with_zero_or_more_data;
i NUMBER := 0;
r_test test_cur%ROWTYPE;
BEGIN
OPEN test_cur;
LOOP
FETCH test_cur INTO r_test;
IF NOT test_cur%ROWCOUNT > 0 THEN
dbms_output.put_line("No data found");
EXIT;
ELSE
dbms_output.put_line("extra row found. Row count is now "||test_cur%ROWCOUNT);
END IF;
...
... -- other routine code if there are rows found.
...
END LOOP;
END;
/
答案 3 :(得分:0)
DECLARE
CURSOR CEMP
IS
SELECT *FROM EMP ;
V_EMP CEMP%ROWTYPE;
BEGIN
OPEN CEMP;
LOOP
FETCH CEMP INTO V_EMP;
IF CEMP%ROWCOUNT=0 or CEMP%ROWCOUNT IS NULL
THEN
DBMS_OUTPUT.PUT_LINE('No Data Found');
END IF;
EXIT WHEN CEMP%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME);
END LOOP;
CLOSE CEMP;
END;
/