是否有替代方法在pl / sql for循环中说'next'?
题
所以我有一个处理ID列表的for循环,并且有一些相当复杂的事情要做。没有涉及所有丑陋的细节,基本上就是这样:
DECLARE l_selected APEX_APPLICATION_GLOBAL.VC_ARR2; ...snip... BEGIN -- get the list ids l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST); -- process each in a nice loop FOR i IN 1..l_selected.count LOOP -- do some data checking stuff... -- here we will look for duplicate entries, so we can noop if duplicate is found BEGIN SELECT county_id INTO v_dup_check FROM org_county_accountable WHERE organization_id = :P4_ID AND county_id = v_county_id; -- NEXT;! NOOP;! but there is no next! EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line('no dups found, proceeding'); END; -- here we have code we only want to execute if there are no dupes already IF v_dup_check IS NULL THEN -- if not a duplicate record, proceed... ELSE -- reset duplicate check variable v_dup_check := NULL; END; END LOOP; END;
我通常如何处理它是通过选择一个值,然后将以下代码包装在IF语句中检查以确保重复的检查变量为NULL。但它很烦人。我只想说下一个;或NOOP;或者其他的东西。特别是因为我已经必须捕获NO_DATA_FOUND异常。我想我可以给Oracle写一封信,但我很好奇别人如何处理这个问题。
我也可以将它包装在一个函数中,但我正在寻找一些更清洁/更简单的东西。
解决方案
也可以计算行数(参见Pourquoi Litytestdata),但您也可以在 when_no_data_found异常
块中执行您想要的操作。
declare
l_selected apex_application_global.vc_arr2;
l_county_id org_county_accountable.count_id%type;
begin
l_selected := apex_util.string_to_table(:p4_select_lst);
for i in l_selected.first..l_selected.last loop
begin
select count_id
into l_county_id
from org_county_accountable
where organization_id = :p4_id
and county_id = v_county_id;
exception
when no_data_found then
-- here we have code we only want to execute if there are no dupes already
-- if not a duplicate record, proceed...
end;
end loop;
end;
其他提示
Oracle 11g增加了C风格的“继续”功能。循环构造到PL / SQL,在语法上听起来就像你正在寻找的那样。
为了您的目的,为什么不在进入循环之前消除重复项?这可以通过使用表函数查询l_selected,然后过滤掉您不想要的记录而不是迭代每个值来完成。有点像...
declare
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
cursor no_dups_cur (p_selected APEX_APPLICATION_GLOBAL.VC_ARR2) is
select * from (
select selected.*,
count(*) over (partition by county_id) cnt -- analytic to find counts grouped by county_id
from table(p_selected) selected -- use table function to treat VC_ARR2 like a table
) where cnt = 1 -- remove records that have duplicate county_ids
;
begin
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
for i in no_dups_cur(l_selected) loop
null; -- do whatever to non-duplicates
end loop;
end;
只需用逻辑代替“重复”即可。与你自己的(你的例子中没有足够的信息真正回答那部分)
而不是捕获 NO_DATA_FOUND
,如何将匹配条目的数量选择到一个变量中,比如说 l_count
,然后继续这个计数是否为零?如下所示:
DECLARE l_selected APEX_APPLICATION_GLOBAL.VC_ARR2; l_count INTEGER; ...snip... BEGIN -- get the list ids l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST); -- process each in a nice loop FOR i IN 1..l_selected.count LOOP -- do some data checking stuff... -- here we will count duplicate entries, so we can noop if duplicate is found SELECT COUNT(*) INTO l_count FROM org_county_accountable WHERE organization_id = :P4_ID AND county_id = v_county_id; IF l_count = 0 THEN -- here we have code we only want to execute if there are no dupes already -- if not a duplicate record, proceed... END IF; END LOOP; END;
<xmp>
<<next_loop>>
loop
...
...
if ....
then
goto next_loop;
</xmp>
这是GOTO语句可能有用的情况。请参阅 Oracle文档控制结构,看看如何做到这一点。此外,您可能希望在此处搜索以了解如何查询记录的存在。运行查询并等待异常不是最佳选择。
另一种方法 - 将支票转换为本地功能:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
...snip...
FUNCTION dup_exists
( p_org_id org_county_accountable.organization_id%TYPE
, p_county_id org_county_accountable.county_id%TYPE
) RETURN BOOLEAN
IS
v_dup_check org_county_accountable.county_id%TYPE;
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = p_org_id AND county_id = p_county_id;
RETURN TRUE;
EXCEPTION WHEN NO_DATA_FOUND THEN
RETURN FALSE;
END;
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
-- do some data checking stuff...
-- here we have code we only want to execute if there are no dupes already
IF NOT dup_exists (:P4_ID, v_county_id) THEN
-- if not a duplicate record, proceed...
END;
END LOOP;
END;
当然,如果您愿意,可以重写本地函数以使用count方法:
FUNCTION dup_exists
( p_org_id org_county_accountable.organization_id%TYPE
, p_county_id org_county_accountable.county_id%TYPE
) RETURN BOOLEAN
IS
l_count INTEGER;
BEGIN
SELECT COUNT(*) INTO l_count
FROM org_county_accountable
WHERE organization_id = p_org_id AND county_id = p_county_id;
RETURN (l_count > 0);
END;
另一种方法是引发和处理用户定义的异常:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
duplicate_org_county EXCEPTION;
...snip...
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
BEGIN
-- do some data checking stuff...
-- here we will look for duplicate entries, so we can noop if duplicate is found
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = :P4_ID AND county_id = v_county_id;
RAISE duplicate_org_county;
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('no dups found, proceeding');
END;
-- here we have code we only want to execute if there are no dupes already
EXCEPTION
WHEN duplicate_org_county THEN NULL;
END;
END LOOP;
END;
我通常不会这样做,但如果有六个理由跳转到下一条记录,这可能比多个嵌套的IF更好。
我知道这是一个老人,但我不禁注意到上面的答案都没有考虑到光标属性:
游标有四个属性:ISOPEN,FOUND,NOTFOUND和ROWCOUNT。可以使用%分隔符访问这些属性以获取有关游标状态的信息。
游标属性的语法是:
cursor_name%attribute
其中cursor_name是显式游标的名称。
因此,在这种情况下,您可以使用ROWCOUNT(表示到目前为止提取的行数),例如:
declare
aux number(10) := 0;
CURSOR cursor_name is select * from table where something;
begin
select count(*) into aux from table where something;
FOR row IN cursor_name LOOP
IF(aux > cursor_name%ROWCOUNT) THEN 'do something is not over';
ELSE 'do something else';
END IF;
END LOOP;
end;