'pl/sql multiple if or multiple begin when data found block
I am new to writing stored procedures. I want to see which is the best way to execute a functionality like below:
cursor c1 is select * from cases where caseid = '2332534534';
begin
for t_case in c1
loop
/..///
if t_case.oCode is not null then
select id into v_ao from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
if v_ao = 0 then
select id into v_ao from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,5)||'%' and rownum=1;
if v_ao = 0 then
select id into v_ao from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,4)||'%' and rownum=1;
if v_ao = 0 then
select id into v_ao from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,3)||'%' and rownum=1;
if v_ao = 0 then
v_ao := '';
end if;
end if;
end if;
end if;
else
v_ao := '';
end if;
/..///
end loop;
commit;
exception
when others then
log_error(0, 'INSERT_case - exception outside' || SQLERRM || ' code ' || SQLCODE, 1);
commit;
end;
This part of code would not work. so instead of select id into v_ao , i will have to check
select count(*) into v_ao from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
if v_ao = 0 then
//do something
else
select id into v_ao from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
end if;
So i am executing 1 query for count and 1 query to get the actual id in the else part. The other way of doing is using begin when no data found execute the 2nd query and inside that no data found open another begin and so on. So basically there will be 5 begin and exception when no data found block which i feel is huge code. Whats the simplest way to do this kind of condition in oracle stored procedure? thanks in advance.
Solution 1:[1]
Get all the matching rows in a single statement (so you do not have to query the table multiple times) and then use a CASE statement to order the rows and only get the best match:
DECLARE
v_substr CASES.OCODE%TYPE;
cursor c1 is select * from cases where caseid = '2332534534';
BEGIN
for t_case in c1
loop
/* ... */
if t_case.oCode is not null then
IF instr(t_case.oCode,',') = 0 THEN
v_substr := UPPER(t_case.oCode);
ELSE
v_substr := UPPER(SUBSTR(t_case.oCode, 1, INSTR(t_case.oCode,',')-1));
END IF;
v_ao := ''; -- same as NULL
BEGIN
SELECT id
INTO v_ao
from lkp_ao
WHERE substr(upper(descr),1,3) = SUBSTR(v_substr, 1, 3)
ORDER BY
CASE
WHEN upper(descr) = v_substr
THEN 1
WHEN substr(upper(descr),1,5) = SUBSTR(v_substr, 1, 5)
THEN 2
WHEN substr(upper(descr),1,4) = SUBSTR(v_substr, 1, 4)
THEN 3
WHEN substr(upper(descr),1,3) = SUBSTR(v_substr, 1, 3)
THEN 4
END
FETCH FIRST ROW ONLY;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END IF;
/* .. */
end loop;
commit;
exception
when others then
log_error(0, 'INSERT_case - exception outside' || SQLERRM || ' code ' || SQLCODE, 1);
commit; -- Really commit after an exception?
END;
/
If you want to make it even more efficient then do all the matching in the cursor query (then you do not have to context switch in every iteration of the cursor between PL/SQL and SQL):
DECLARE
v_substr CASES.OCODE%TYPE;
CURSOR c1 IS
SELECT c.*,
l.id AS ao
FROM (
SELECT c.*,
CASE INSTR(oCode, ',')
WHEN 0 THEN oCode
ELSE SUBSTR(oCode, 1, INSTR(oCode, ',') - 1)
END AS term
FROM cases c
) c
LEFT OUTER JOIN LATERAL (
SELECT id
from lkp_ao a
WHERE substr(upper(a.descr),1,3) = SUBSTR(c.term, 1, 3)
ORDER BY
CASE
WHEN upper(a.descr) = c.term
THEN 1
WHEN substr(upper(a.descr),1,5) = SUBSTR(c.term, 1, 5)
THEN 2
WHEN substr(upper(a.descr),1,4) = SUBSTR(c.term, 1, 4)
THEN 3
WHEN substr(upper(a.descr),1,3) = SUBSTR(c.term, 1, 3)
THEN 4
END
FETCH FIRST ROW ONLY
) l
ON (1 = 1)
WHERE caseid = '2332534534';
BEGIN
for t_case in c1
loop
/* ... */
v_ao := t_case.ao;
/* .. */
end loop;
commit;
exception
when others then
log_error(0, 'INSERT_case - exception outside' || SQLERRM || ' code ' || SQLCODE, 1);
commit; -- Really commit after an exception?
END;
/
Solution 2:[2]
Well,
if you declare additional variables and run all
selectstatements (each of them returning its ownv_ao#value) andapply the
maxfunction so that query wouldn't end up withNO_DATA_FOUND; it'll returnNULLinsteadSQL> select 1 from dual where 1 = 2; no rows selected SQL> select max(1) From dual where 1 = 2; MAX(1) ---------- SQL>then you wouldn't have to enclose each
selectinto its ownbegin-exception-endblockuse nested
CASEexpressions to return the final result
Something like this:
declare
v_ao1 number;
v_ao2 number;
v_ao3 number;
v_ao4 number;
v_ao number;
begin
for t_case in c1 loop
select max(id) into v_ao1 from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
select max(id) into v_ao2 from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,5)||'%' and rownum=1;
select max(id) into v_ao3 from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,4)||'%' and rownum=1;
select max(id) into v_ao4 from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,3)||'%' and rownum=1;
v_ao := case when nvl(v_ao1, 0) = 0 then
case when nvl(v_ao2, 0) = 0 then
case when nvl(v_ao3, 0) = 0 then
case when nvl(v_ao4, 0) = 0 then null
else v_ao4
end
else v_ao3
end
else v_ao2
end
else v_ao1
end;
end loop;
end;
Solution 3:[3]
Sometimes, in such cases, it's better to get length of longest match. One of the variants for this is to use UTL_RAW.BIT_XOR:
with
t(s) as (
select 'AAAABB' from dual union all
select 'AAAABC' from dual union all
select 'AAABBC' from dual union all
select 'BBBXXXX' from dual union all
select 'BBBYYY' from dual
)
,search_strings(str) as (
select 'AAAAB' from dual union all
select 'BBBZZZ' from dual
)
select
t.s,s.str,
utl_raw.bit_xor(utl_raw.cast_to_raw(t.s),utl_raw.cast_to_raw(s.str)) s_xor,
length(
regexp_substr(
utl_raw.bit_xor(utl_raw.cast_to_raw(t.s),utl_raw.cast_to_raw(s.str))
,'^(00)+'
)
)/2 as n_matches
from t, search_strings s
;
Results:
S STR S_XOR N_MATCHES
------- ------ ------------------------------ ----------
AAAABB AAAAB 000000000042 5
AAAABC AAAAB 000000000043 5
AAABBC AAAAB 000000030043 3
BBBXXXX AAAAB 030303191A5858
BBBYYY AAAAB 030303181B59
AAAABB BBBZZZ 0303031B1818
AAAABC BBBZZZ 0303031B1819
AAABBC BBBZZZ 030303181819
BBBXXXX BBBZZZ 00000002020258 3
BBBYYY BBBZZZ 000000030303 3
10 rows selected.
As you can see, BIT_XOR returns '00' for equal chars so you can count a number of 00 in it. Column n_matches returns this number of matched symbols
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Littlefoot |
| Solution 3 | Sayan Malakshinov |
