Gibt es alternative Methoden zu sagen 'next' in einem PL / SQL-for-Schleife?
Frage
So eine habe ich für Schleife bekam, die eine Liste von IDs verarbeitet und hat einige ziemlich komplexe Dinge zu tun. Ohne auf alle die hässlichen Details, im Grunde diese:
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;
Wie ich normalerweise umgehen dies, indem sie in einen Wert auswählen und dann den folgenden Code in einer IF-Anweisung wickelt Überprüfung, um sicherzustellen, dass doppelte Kontrolle Variable NULL ist. Aber es ist ärgerlich. Ich möchte nur in der Lage sein, als nächstes zu sagen; oder NOOP; oder so. Vor allem, da muss ich schon die NO_DATA_FOUND Ausnahme fangen. Ich glaube, ich könnte einen Brief an Oracle schreiben, aber ich bin neugierig, wie andere damit umgehen.
Ich könnte auch in einer Funktion wickle dies auch, aber ich war auf der Suche nach etwas ein wenig sauberen / einfacher.
Lösung
Um die Anzahl der Zeilen ist ebenfalls möglich (siehe Pourquoi Litytestdata) zu zählen, aber Sie können auch tun, was Sie in dem when_no_data_found exception
Block tun wollen.
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;
Andere Tipps
Oracle 11g fügt ein C-Stil „weiter“ Schleife zu PL / SQL-Konstrukt, das syntaktisch klingt wie das, was Sie suchen.
Für Ihre Zwecke, warum beseitigen nicht nur die Duplikate, bevor die Schleife eintritt? Dies könnte geschehen durch abfragt l_selected eine Tabellenfunktion verwenden, und dann Datensätze herausgefiltert werden Sie nicht wollen, statt iterieren alle Wert. So etwas wie ...
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;
Nur die Logik ersetzen für die Bestimmung eines „Duplikat“ mit Ihrem eigenen (nicht genügend Informationen aus Ihrem Beispiel hat wirklich, dass ein Teil zu beantworten)
Stattdessen fängt NO_DATA_FOUND
, wie etwa die Anzahl der passenden Einträge in eine Variable Auswählen, sagt l_count
und fortfahren, wenn diese Zählung Null sein klappt? Etwas wie folgt aus:
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>
Dies ist ein Fall, in dem eine GOTO-Anweisung könnte nützlich sein. Sehen Sie sich die Oracle Dokumentation in der Kontrollstrukturen zu sehen, wie dies zu tun. Außerdem können Sie hier in der Nähe suchen, um herauszufinden, wie die Existenz eines Datensatzes abfragen. eine Abfrage und wartet auf eine Ausnahme läuft nicht optimal ist.
Eine andere Art und Weise - drehen Sie den Scheck in eine lokale Funktion:
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;
Natürlich könnte die lokale Funktion neu geschrieben werden, um die Zählmethode zu verwenden, wenn Sie bevorzugen:
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;
Eine andere Methode ist eine benutzerdefinierte Ausnahme zu heben und zu behandeln:
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;
Ich würde dies normalerweise nicht tun, aber wenn es ein halbes Dutzend Gründe dafür waren zum nächsten Datensatz zu springen, kann dies auf mehrere verschachtelte IFs vorzuziehen.
Ich weiß, dass dies ein Oldie, aber ich konnte nicht helfen, dass keine der Antworten bemerken oben in Betracht ziehen Sie den Cursor Attribute :
Es gibt vier Attribute mit Cursor zugeordnet: ISOPEN, GEFUNDEN, NOTFOUND und ROWCOUNT. Diese Attribute können mit dem% delimiter zugegriffen werden, um Informationen über den Zustand des Cursors zu erhalten.
Die Syntax für ein Cursor-Attribut ist:
cursor_name%attribute
wo cursor_name ist der Name des expliziten Cursor.
Also in diesem Fall könnten Sie ROWCOUNT (die die Anzahl der Zeilen angibt, so weit hergeholt) für Ihre Zwecke, wie folgt aus:
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;