How to dynamically store the value of a loop and return it once the for loop breaks? - sql

I have a code something like this
FOR K IN (SELECT E.COLUMN_VALUE
FROM TABLE (SELECT CAST(LEAVE_HOLIDAY_CAL_PKG_NEW.GES_LEV_COLUMN_TO_ROWS_FNC(V_CAL_SUBTSR,
',') AS
LEV_TABLE_OF_VARCHAR_TYP)
FROM DUAL) E) LOOP
V_CAL_DESC := CASE WHEN K.COLUMN_VALUE = 'W' THEN 'Project Weekend-' || TO_CHAR((V_DATE1 + V_ITERATION), 'Day') WHEN K.COLUMN_VALUE = 'H' THEN 'Holiday' ELSE 'Working day' END;
IF K.COLUMN_VALUE = 'H' THEN
FETCH C_HOLIDAY_CURSR
INTO V_HOLIDAY_ID;
SELECT H.HOLIDAY_DESC
INTO V_CAL_DESC
FROM LEAVE.LEV_NEW_EMP_HOLIDAY_DTLS H
WHERE H.HOLIDAY_ID = V_HOLIDAY_ID;
END IF;
INSERT INTO LEV_CAL_TEMP_TBL
VALUES
(IN_PERSON_ID, K.COLUMN_VALUE, V_DATE1 + V_ITERATION, V_CAL_DESC);
COMMIT;
V_ITERATION := V_ITERATION + 1;
END LOOP;
OPEN C_CAL_CURSR FOR
select *from LEV_CAL_TEMP_TBL;
So every time this for loop runs one row is inserted in the table GES_LEV_CAL_TEMP_TBL.
But I wanted to avoid this as this might affect the performance of the system,So is there any way I can store those value somewhere
and return the same in cursor once the FOR loop ends.
Thanks a lot in advance.

A cursor FOR LOOP is row-by-row a.k.a slow-by-slow process.
If you must do it in PL/SQL, then use the BULK COLLECT and FORALL statement.
Declare a collection type:
For example,
TYPE t_lev_cal_temp_tbl IS TABLE OF lev_cal_temp_tbl%ROWTYPE;
l_lev_cal t_lev_cal_temp_tbl := t_lev_cal_temp_tbl();
FORALL insert statement:
For example,
FORALL i IN l_lev_cal.first .. l_lev_cal.last
INSERT INTO lev_cal_temp_tbl VALUES l_tab(i);

Related

Error while writing to array plsql how to fix? Extend doesn't work also

so I am trying to write to an array in PL/SQL, and I always get the subscript outside of limit error. I've seen similar posts and implemented everything based on those answers, I can't seem to find what I'm doing wrong. The line giving the error is "arr_quartosLivres(counter) := q.id;" I've tried to extend the array and it still doesn't work, however, either way, the look only runs 21 times (because there are only 21 values in the table quarto) so it shouldn't even need to be extended. Any help would be highly appreciated! Thank you
SET SERVEROUTPUT ON;
DECLARE
p_idReserva reserva.id%type := 408;
v_dataEntradaReserva reserva.data_entrada%type;
counter integer := 0;
type arr_aux IS varray(21) of quarto.id%type;
arr_quartosLivres arr_aux := arr_aux();
BEGIN
SELECT data_entrada INTO v_dataEntradaReserva FROM reserva WHERE id = p_idreserva;
FOR q IN (SELECT * FROM quarto)
LOOP
BEGIN
IF isQuartoIndisponivel(q.id, v_dataEntradaReserva)
THEN DBMS_OUTPUT.PUT_LINE('nao disponivel' || counter);
arr_quartosLivres(counter) := q.id;
ELSE DBMS_OUTPUT.PUT_LINE('disponivel' || counter);
END IF;
counter := counter + 1;
END;
END LOOP;
END;
The index values for varray begin with 1. Your logic is trying to use index value 0. Thus index out of range. BTW extend does not apply to varray, when declared a varray has a fixed size. You have 3 solutions: initialize counter to 1 instead of 0, or move incrementing it prior to its use as an index. Since as it stands you increment every time through the loop, even when the IF condition returns false and you do not use the counter as an index, leaving a NULL value in the array.But you use counter for 2 different purposes: Counting rows processed and index into the array. Since the row value may not be put into the array then your 3rd option is to introduce another variable for the index. Further there is no need for the BEGIN ... End block in the loop.
declare
p_idreserva reserva.id%type := 408;
v_dataentradareserva reserva.data_entrada%type;
counter integer := 0;
type arr_aux is varray(21) of quarto.id%type;
arr_quartoslivres arr_aux := arr_aux();
varray_index integer := 1 ; -- index varaibal for varray.
begin
select data_entrada into v_dataentradareserva from reserva where id = p_idreserva;
for q in (select * from quarto)
loop
if isquartoindisponivel(q.id, v_dataentradareserva)
then
dbms_output.put_line('nao disponivel' || counter || ' at index ' || varray_index);
arr_quartoslivres(varray_index) := q.id;
varray_index := varray_index + 1;
else
dbms_output.put_line('disponivel' || counter);
end if;
counter := counter + 1;
end loop;
end;

How to use two columns with clause definescore oracle text

I have this code:
declare
sName varchar(25);
iRank number := 0;
sDesc varchar(510);
cursor q is
SELECT *
FROM trec_topics ORDER BY num;
BEGIN
for ql in q
loop
sDesc := replace(replace(replace(ql.title, '?', '{?}'), ')', '{)}'), '(', '{(}');
--dbms_output.put_line(ql.num||'-'||sDesc);
declare
cursor c is
SELECT /*+ FIRST_ROWS(100) */ docno,
CASE
WHEN SCORE(10) >= SCORE(20) THEN SCORE(10)
ELSE SCORE(20)
END AS SCORE
FROM txt_search_docs WHERE CONTAINS(txt, 'DEFINESCORE(ql.title, OCCURRENCE)', 10) > 0 OR
CONTAINS(txt, 'DEFINESCORE(sDesc, OCCURRENCE)', 20) > 0
order by SCORE desc;
begin
iRank := 1;
for c1 in c
loop
dbms_output.put_line(ql.num||' Q0 '||c1.docno||' '||lpad(iRank,3, '0')||' '||lpad(c1.score, 2, '0')||' myUser');
iRank := iRank + 1;
exit when c%rowcount = 100;
end loop;
end;
end loop;
end;
As you can see I'm doing select on two different tables, however, I need to change the standard score, as it did not perform well. I'm trying to use the DEFINESCORE clause that has this 'DEFINESCORE (query_term, scoring_expression)' format.
How can I call the table columns within this clause? That is, I need to call my columns instead of "query_term", as there are several documents to do the search. Because the way I’m calling him, he’s looking for exactly the term ql.title
Anyone a suggestion to help me with this problem?
I finally managed to solve it.
It was about:
create a variable: topics varchar (525);
store the column value: topics := replace(replace(replace(ql.title, '?', '{?}'), ')', '{)}'), '(', '{(}');
and after calling it in the CONTAINS clause: FROM txt_search_docs WHERE CONTAINS(txt, 'DEFINESCORE(('''||topics||'''), OCCURRENCE)', 1) > 0

Performance improvement of plsql using Bulk collect

I am using bulk collect to improve execution time. When I do not use bulk collect, it executes in 4 mins.
But when I use bulk collect there is no output, neither the error message is shown in console. I can see a blank spool file created.
Please let me know if I have utilized bulk collect incorrectly, also can we use this clause in select statement with limit?
Table consists of maximum 1 million records.
SET SERVEROUTPUT ON FORMAT WRAPPED
SET VERIFY OFF
SET FEEDBACK OFF
SET TERMOUT OFF
SPOOL C:\Temp\spool_1.txt
DECLARE
cursor c2 is (
select count(distinct e.cdb_pref_event_id)
,e.supp_cd
from (select distinct eh.cdb_customer_id cdb_customer_id
,eh.cdb_pref_event_id cdb_pref_event_id
,eh.supp_cd supp_cd
from (select *
from cdb_stg.cpm_pref_event_stg_arc
where trunc(load_date) = trunc(sysdate - 1)) eh
Left outer join cdb_admin.cpm_pref_result er on (eh.cdb_customer_id =
er.cdb_customer_id and
eh.cdb_pref_event_id =
er.cdb_pref_event_id)
where er.cdb_pref_event_id is null
and er.cdb_customer_id is null) r
join cdb_admin.cpm_pref_event_exception e on (r.cdb_customer_id =
e.cdb_customer_id and
r.cdb_pref_event_id =
e.cdb_pref_event_id)
group by e.supp_cd);
TYPE totalprefresults is table of NUMBER(20);
TYPE supcd_1 is table of cdb_admin.cpm_pref_event_stg.supp_cd%TYPE;
total_prefresults totalprefresults;
supcd1 supcd_1;
--Total_prefresults NUMBER(20);
--SUPCD1 CDB_ADMIN.CPM_PREF_EVENT_STG.supp_cd%TYPE;
profile_counts NUMBER(20);
iter Integer := 0;
BEGIN
select count(distinct cdb_customer_id)
into profile_counts
from cdb_admin.cpm_pref_event_exception h
where cdb_customer_id in
(Select distinct e.cdb_customer_id
from (Select distinct eh.cdb_customer_id cdb_customer_id
,eh.cdb_pref_event_id cdb_pref_event_id
,eh.supp_cd supp_cd
from (select *
from cdb_stg.cpm_pref_event_stg_arc
where trunc(load_date) = trunc(sysdate - 1)) eh
Left outer join cdb_admin.cpm_pref_result er on (eh.cdb_customer_id =
er.cdb_customer_id and
eh.cdb_pref_event_id =
er.cdb_pref_event_id)
where er.cdb_pref_event_id is null
and er.cdb_customer_id is null) r
join cdb_admin.cpm_pref_event_exception e on (r.cdb_customer_id =
e.cdb_customer_id and
r.cdb_pref_event_id =
e.cdb_pref_event_id)
where e.supp_cd = 'PROFILE-NOT-FOUND')
and h.supp_cd != 'PROFILE-NOT-FOUND';
dbms_output.put_line('TOTAL EVENTS VALIDATION');
dbms_output.put_line('-------------------------------------------------------------');
dbms_output.put_line('');
dbms_output.put_line(rpad('Pref_Counts', 25) || rpad('Supp_CD', 25));
OPEN c2;
LOOP
FETCH c2 BULK COLLECT
INTO total_prefresults
,supcd1 limit 100;
EXIT WHEN c2%NOTFOUND;
dbms_output.put_line(rpad(total_prefresults, 25) || rpad(supcd1, 25));
IF (supcd1 = 'PROFILE-NOT-FOUND')
then
dbms_output.put_line('');
dbms_output.put_line('Profile not found records count : ' ||
total_prefresults);
dbms_output.put_line(profile_counts ||
' : counts moved to other exceptions ');
dbms_output.put_line((total_prefresults - profile_counts) ||
' : are still in Profile_not_found exception');
END IF;
iter := iter + 1;
END LOOP;
CLOSE c2;
dbms_output.put_line('');
dbms_output.put_line('Number of missing Records: ' || iter);
END;
/
SPOOL OFF
I think the bottleneck is this condition: where trunc(load_date) = trunc(sysdate - 1)
Do you haven an index on trunc(load_date)? Either create a function-based index on trunc(load_date) or if you already have an index on load_date then try
WHERE load_date >= trunc(sysdate - 1) AND load_date < trunc(sysdate)
Also check your queries whether distinct is really needed. Remove them, if possible.
I have reframed your code starting from OPEN c2; to CLOSE c2;
BULK COLLECT should be executed to store all the data in the collection only once(in one go) and then this collection can be used using the index(i.e. I in the following case) in FOR loop as follows:
OPEN C2;
FETCH C2 BULK COLLECT INTO
TOTAL_PREFRESULTS,
SUPCD1;
--EXIT WHEN C2%NOTFOUND;
CLOSE C2;
-- To list down all the values before processing the logic
FOR I IN TOTAL_PREFRESULTS.FIRST..TOTAL_PREFRESULTS.LAST LOOP
DBMS_OUTPUT.PUT_LINE(RPAD(TOTAL_PREFRESULTS(I), 25)
|| RPAD(SUPCD1(I), 25));
END LOOP;
FOR I IN TOTAL_PREFRESULTS.FIRST..TOTAL_PREFRESULTS.LAST LOOP
IF ( SUPCD1(I) = 'PROFILE-NOT-FOUND' ) THEN
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('Profile not found records count : ' || TOTAL_PREFRESULTS(I));
DBMS_OUTPUT.PUT_LINE(PROFILE_COUNTS || ' : counts moved to other exceptions ');
DBMS_OUTPUT.PUT_LINE((TOTAL_PREFRESULTS(I) - PROFILE_COUNTS)
|| ' : are still in Profile_not_found exception');
END IF;
ITER := ITER + 1;
END LOOP;
replace above code snippet in your code and try to execute.
Refer the guide to use BULK COLLECT
Cheers!!
Bulk collect can provide considerable performance gain. However there are a couple gotchas involved.
First off there is the difference in the meaning of %notfound.
On a standard cursor %notfound means all rows have already been fetched and there is no more. With bulk collect this changes to 'there were insufficient rows to reach the LIMIT specified (if present).
That does NOT mean there no rows fetched just the limit specified was not reached. For example if your limit is 100 and the fetch retrieved only 50 then %notfound would return True. This is where the referenced guide fails.
The second is what happens without the limit clause: All rows from the cursor are returned into shared memory(PGA?). So what's the problem with that.
If there 100 rows or 1000 then most likely you're k, but suppose there are 100,000 or 1M rows, they are still all loaded into memory. Finally (at least for now) when the limit clause is used then the entire Fetch+Process must itself be enclosed within a loop or you process only the first fetch - meaning only the specified limit number of rows - no matter how many actually exist. Another point where the referenced guide fails.
The following skeleton accommodates the above.
declare
max_bulk_rows constant integer := 1000; -- define the max number of rows for each fetch ...
cursor c_bulk is(
Select ... ;
type bulk_row_t is table of c_bulk%rowtype;
bulk_row bulk_row_t;
Begin
open c_bulk;
loop
fetch c_bulk -- fill buffer
bulk collect into bulk_row
limit max_bulk_row;
for i in bulk_row.first .. bulk_row.last -- process each row in buffer
loop
"process individual row here"
end loop;
foreach ... -- bulk output of rows here is needed.
exit when bulk_row.count < max_bulk_row; -- exit process loop if all rows processed
end loop ; -- loop back and fetch next buffer if needed
close c_bulk;
...
end;

Database PL/SQL

I have to make an assignment for school but I get two errors:
Encountered the symbol "FETCH" when expecting on of the following:
constant exception <an identifier> <a double-quoted
delimited-identifier> table LONG_ double ref char time timestamp
interval date binary national character nchar
and
Encountered the symbol "end-of-file" when expecting one of the
following: end not pragma final instantiable order overriding static
member constructor map
Here is the link to my code: http://pastebin.com/h4JN9YQY
CREATE OR REPLACE PROCEDURE generate_bonus
AS
cursor student_info is
select distinct students.id,
events.begindatetime,
events.enddatetime,
count(items.number_of_coupons) as coupons_collected,
events.type from students
join applies on applies.students_id = students.id
join schedules on schedules.id = applies.schedules_id
join events on events.id = schedules.events_id
join orders on orders.students_id = students.id
join orderitems on orderitems.orders_id = orders.id
join items on items.id = orderitems.items_id
join bars on bars.id = orders.bars_id
where applies.status = 'PLANNED'
and orderitems."NUMBER" is not null
and bars.name is not null
group by students.id, events.begindatetime, events.enddatetime, events.type
order by students.id;
BEGIN
DECLARE
s_id integer(256);
s_beginDate date;
s_endDate date;
s_noCoupons number(256);
s_eventType varchar2(256);
s_workedHours number(24) := 8;
calculated_bonus number(256);
count_rows integer(256);
OPEN student_info;
LOOP
FETCH student_info into s_id, s_beginDate, s_endDate, s_noCoupons, s_eventType;
Select count(*) into count_rows from student_bonus where students_id = s_id and rownum <= 1;
EXIT WHEN count_rows = 1;
IF (s_eventType = 'ROUGH') THEN
calculated_bonus := s_workedHours * (s_workedHours / 100 * 7) * s_noCoupons;
INSERT INTO student_bonus(students_id, bonus, events_id) VALUES (s_id, calculated_bonus, s_eventType);
calculated_bonus := 0;
ELSIF (s_eventType = 'NORMAL') THEN
calculated_bonus := s_workedHours * (s_workedHours / 100 * 4) * s_noCoupons;
INSERT INTO student_bonus(students_id, bonus, events_id) VALUES (s_id, calculated_bonus, s_eventType);
calculated_bonus := 0;
ELSE
calculated_bonus := s_workedHours * (s_workedHours / 100 * 2) * s_noCoupons;
INSERT INTO student_bonus(students_id, bonus, events_id) VALUES (s_id, calculated_bonus, s_eventType);
calculated_bonus := 0;
END IF;
END LOOP;
CLOSE student_info;
END generate_bonus;
In my opinion much easier for juniors to use is cursor loop, in this project You will avoid this kind of errors. Syntax like:
FOR row_variable IN cursor LOOP
dbms_output.put_line(row_variable.id);
END LOOP;
row_variable holds valeus from each cursor row, and you can easily acces it with '.' (dot) operator like row_variable.id
Using cursor loop lets You avoid problems with fetching data, taking care about open/close cursor and worry about output outside cursor space.
The loop will make loops exactly how many items cursor is pointing, like for each loop.
put this line:
EXIT WHEN student_info%NOTFOUND;
after this line:
FETCH student_info into s_id, s_beginDate, s_endDate, s_noCoupons, s_eventType;
You had reached the end of your cursor but there is no code which tells it to exit.
Cursor should follow steps in following manner..
Open Cursor
LOOP
FETCH cursor
EXIT Cursor condtion
{--
Your rest of the code here
--}
end loop;

Oracle pl/sql Change Array Size and Members After Delete

For example i have an array like
"a(1):=1 ,a(2):=2, a(3) := 3"
and now my array count =3 "(a.count)"
then i delete middle member "a.delete(2)" then i wanna make my array like this "a(1):=1;a(2):=3" and my array count = 2 ("a.count") how can i do this ?
ps:i need to this with big sized array so i think i should use, for or while loop but how...
The collection where you have deleted some element is called sparse collection. Below you have example how to iterate that type of collection and how to use it with forall.
declare
type a is table of number;
ar a;
v_idx number;
begin
select level bulk collect into ar from dual connect by level< 1000;
ar.delete(1);
ar.delete(4);
ar.delete(10);
ar.delete(88);
v_idx := ar.first;
while v_idx is not null loop
dbms_output.put_line('idx: '||v_idx ||' value:'|| ar(v_idx));
v_idx := ar.next(v_idx);
end loop;
-- FORALL i IN INDICES OF ar
-- INSERT INTO test_table VALUES ar(i);
end;
Thank you but i should change array too , i need to take same output when i print array members like
for i in ar.first..ar.last loop
dbms_output.put_line(ar(i));
end loop;
declare
type a is table of number;
ar a;
begin
select level bulk collect into ar from dual connect by level< 1000;
ar.delete(1);
ar.delete(4);
ar.delete(10);
ar.delete(88);
-- ar is sparse collection;
ar := ar MULTISET intersect ar;
-- ar is dense collection and for i in .... is possible
FOR i IN ar.first .. ar.last LOOP
DBMS_OUTPUT.put_line(ar(i));
END LOOP;
end;
you can try this approach assign values of first spared collection to second continues collection and use second collection for further processing...
declare
type num_arr is table of number;
v_num_arr1 num_arr; --first collection
v_num_arr2 num_arr := num_arr(); -- second collection initialization and declaration
v_idx number;
v_col_index number := 1;
begin
-- fill 10 element.
select level || '1' as num1 bulk collect into v_num_arr1 from dual connect by level < 10;
for x in v_num_arr1.first .. v_num_arr1.last loop
dbms_output.put_line('index: ' || x || ' value: ' || v_num_arr1(x));
end loop;
dbms_output.put_line('');
-- delete element
v_num_arr1.delete(3);
v_num_arr1.delete(7);
v_idx := v_num_arr1.first;
while v_idx is not null loop
dbms_output.put_line('index: ' || v_idx || ' value: ' || v_num_arr1(v_idx));
-- filling second collection with regular index by variable v_col_index
if v_num_arr1(v_idx) is not null then
v_num_arr2.extend(1);
v_num_arr2(v_col_index) := v_num_arr1(v_idx);
v_col_index := v_col_index + 1;
end if;
v_idx := v_num_arr1.next(v_idx);
end loop;
dbms_output.put_line('second collection elements
');
--check second colleciton
for x in v_num_arr2.first .. v_num_arr2.last loop
dbms_output.put_line('index: ' || x || ' value: ' || v_num_arr2(x));
end loop;
end;