I want to update the table, however, collections are going into a loop. Need to update 500 000 records, but it is taking a lot of time..if something can be done..
CREATE OR REPLACE PROCEDURE PROC_ACCOUNT_STATUS AS
CURSOR C1 IS
SELECT ACCOUNTS1,
abs((PREVIOUS_DELINQUENCIES - CURRENT_DELINQUENCIES)) AS DIFF_DEL
FROM TEMP_LOAN;
TYPE COLL_ACCOUNT_STATUS IS TABLE OF C1%ROWTYPE;
COLL_STAB1 COLL_ACCOUNT_STATUS := COLL_ACCOUNT_STATUS();
COLL_STAB2 COLL_ACCOUNT_STATUS := COLL_ACCOUNT_STATUS();
BEGIN
OPEN C1;
LOOP
FETCH C1 BULK COLLECT INTO COLL_STAB2 LIMIT 500;
EXIT WHEN COLL_STAB2.COUNT = 0;
COLL_STAB1 := COLL_STAB2;
END LOOP;
CLOSE C1;
FOR I IN 1..COLL_STAB1.COUNT
LOOP
IF(COLL_STAB1(I).DIFF_DEL>=30) AND(COLL_STAB1(I).DIFF_DEL>=31) THEN
COLL_STAB1.EXTEND();
COLL_STAB1(COLL_STAB1.COUNT):=COLL_STAB1(I);
END IF;
END LOOP;
FORALL I IN 1..COLL_STAB1.COUNT
UPDATE TEMP_LOAN
SET ACCOUNT_STATUS = 'STAB'
WHERE ACCOUNTS1 = COLL_STAB1(I).ACCOUNTS1;
COLL_STAB1.DELETE;
COLL_STAB2.DELETE;
COMMIT;
END;
What is wrong with a straightforward update?
update temp_loan
set ACCOUNT_STATUS = 'STAB'
WHERE SUBSTR((PREVIOUS_DELINQUENCIES - CURRENT_DELINQUENCIES),2) >= 31
Possibly this is not the answer you want, especially if (as per my comment) the logic you posted in the question is not representative of the logic you actually are implementing.
Related
i dont kno whow to write a proper procedure wtih cursors. i am doing somehtings wring ?
procedure1: for given student show all titles of books he ever rented
--no such student no recordsexcep
SET SERVEROUTPUT ON
create or replace procedure showRENTS(v_idStudent int)
as
v_NameOfBook varchar2(100);
--v_count int;
no_such_id EXCEPTION;
cursor c1 is
select NameOfbook from book
where
begin
open c1;
loop
fetch c1
into v_fname,v_lname;
--select count(1) into v_count from Student where idStduent =v_id ;
--if
--v_count = 0;
--then raise no_such_id;
--end if;
exit when c1%notfounf
end loop;
close c1;
end;
/
I can't see images, but - procedure you posted can be rewritten to
CREATE OR REPLACE PROCEDURE showrents (v_idstudent IN INT)
AS
BEGIN
FOR c1 IN (SELECT nameofbook FROM book)
LOOP
DBMS_OUTPUT.put_line (c1.nameofbook);
END LOOP;
END;
/
I'd suggest you to use cursor FOR loop - it is simpler to write & maintain as Oracle does most of job for you (you don't have to declare cursor variable, open the cursor, fetch from it, take care about exiting the loop, closing the cursor).
I'm not sure what its parameter has to do with it but I left it; I guess you'll add some code later.
I hope it'll get you started.
For issues like this just follow your ERD. So here your given a student is and asked for the book rented. Follow the path: Student -> Process -> Copies -> Book. It actually becomes a fairly easy query.
create or replace procedure showrents(v_idStudent int)
as
no_such_id EXCEPTION;
v_student_ok interger :=0;
cursor c_student_books is
select nameofbook
from student s
join process p on (p.idstudent = s.idstudent)
join copies c on (c.idcopies = p.idcopies)
join books b on (b.idbook = b.idbook)
where s.idstudent = v_idStudent;
begin
select count(*)
into v_student_ok
from student
where idstudent = v_idstudent
and rownum <= 1;
if v_student_ok = 0
then
raise no_such_id;
end if;
for rented in c_student_books
loop
dbms_output.put_line( rented.nameofBook);
end ;
end showrents; ```
I am very new to PL/SQL and I am trying to use an explicit cursor to iterate over my database, FLEX_PANEL_INSPECTIONS. I would like to fetch each row from the database in turn using an explicit cursor, and depending on the randomly generated 'status' of a given 'panel' in the row, assign the panel a new status value within an if / else statement. The status of the panel is random but Boolean - it is either 1 or 0.
However, when I view the DBMS output, I note that the fetch does not retrieve all values from the database - only those who have a status value of 1. I have included the core code below.
I would be very grateful if anybody is able to help me find a solution, or explain the root cause of my problem, thanks!
create or replace procedure FLEX_SUMMARY_STATUS_PROCEDURE as
old_panel_status number;
new_panel_status number;
cursor panel_cursor is
select FLEX_PANEL_STATUS
from FLEX_PANEL_INSPECTIONS;
begin
open panel_cursor;
loop
fetch panel_cursor into old_panel_status;
exit when panel_cursor%notfound;
if old_panel_status = 0
then new_panel_status := 2;
elsif old_panel_status = 1
then new_panel_status := 3;
--More conditional loops follow (but are irrelevant for this question).
dbms_output.put_line(old_panel_status);
--Test output
--This displays all of the 1's that were randomly generated in the original table.
--It does not display any of the 0's that were generated.
end if;
end loop;
close panel_cursor;
close sensor_cursor;
end FLEX_SUMMARY_STATUS_PROCEDURE;
/
In addition to the main error, which has already been fixed in the accepted answer, the below code shows the newer loop construct. Instead of rec, you can choose any variable name you like. On each iteration, it contains a row (usually with more than one column).
create or replace procedure FLEX_SUMMARY_STATUS_PROCEDURE as
new_panel_status number;
cursor panel_cursor is
select FLEX_PANEL_STATUS
from FLEX_PANEL_INSPECTIONS;
begin
for rec in panel_cursor loop
if rec.flex_panel_status = 0 then
new_panel_status := 2;
elsif rec.flex_panel_status = 1 then
new_panel_status := 3;
--More conditional loops follow (but are irrelevant for this question)
end if;
dbms_output.put_line(rec.flex_panel_status);
end loop;
end FLEX_SUMMARY_STATUS_PROCEDURE;
/
You can even get rid if the explicit cursor if you like:
for rec in (
select FLEX_PANEL_STATUS
from FLEX_PANEL_INSPECTIONS
) loop
If you didn't make a mistake when removing the additional elseif clauses, the issue is in the location of your dbms_output.put_line.
It's located inside the else part, so will only trigger when this clause is called. Move it below the END IF and make sure to use proper indentation, which makes such things way easier to spot.
create or replace procedure FLEX_SUMMARY_STATUS_PROCEDURE as
old_panel_status number;
new_panel_status number;
cursor panel_cursor is
select FLEX_PANEL_STATUS
from FLEX_PANEL_INSPECTIONS;
begin
open panel_cursor;
loop
fetch panel_cursor into old_panel_status;
exit when panel_cursor%notfound;
if old_panel_status = 0
then new_panel_status := 2;
elsif old_panel_status = 1
then new_panel_status := 3;
--More conditional loops follow (but are irrelevant for this question)
end if;
dbms_output.put_line(old_panel_status);
end loop;
close panel_cursor;
close sensor_cursor;
end FLEX_SUMMARY_STATUS_PROCEDURE;
/
I am trying to update 5 million rows. Below query runs in 5-6 minutes. But I want to have periodic commit in between 500000 records. How do i do that?
Any help is appreciated.
Thanks
DECLARE
a NUMBER;
BEGIN
UPDATE table1
SET (name) =
(SELECT name
FROM table1
WHERE a1= 24672
WHERE ROWNUM <= 6500000;
a := SQL%ROWCOUNT;
DBMS_OUTPUT.put_line (a || ' Rows Updated');
END;
/
Since you have tagged this 'optimization' I assume you care about performance. Whilst you could rewrite your SQL in PL/SQL, use a loop and commit every n iterations, this is going to slow you down.
The fastest way to update millions of rows is often in fact not to update at all. Instead you create a new table (CREATE TABLE ... AS ... SELECT ), drop your old table and then rename your new table. It reduces the amount of redo and undo and greatly speeds up performance.
See How to update millions of rows
If performance is good enough then possibly you no longer care about partial commits?
You could probably do something similar to this.
DECLARE
a NUMBER;
commitCt NUMBER;
rowCt NUMBER;
BEGIN
LOOP
commitCt := 0;
rowCt := 0;
SAVEPOINT svePoint;
WHILE rowCt/500000 <= 1
LOOP
BEGIN
rowCt := rowCt + 1;
commitCt := commitCt + 1;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK to svePoint;
END;
END LOOP;
COMMIT;
IF commitCt = 0 THEN
EXIT;
END IF;
END LOOP;
END;
/
I have a table named XXALD_INT_IN_VEHICLE_ITEM, in which there are 11 lines.
I define a cursor:
CURSOR LINE_VEH IS
SELECT *
FROM XXALD_INT_IN_VEHICLE_ITEM
FOR UPDATE
;
I take the lines one by one, and handle it, then update a field of this line(error_code).
OPEN LINE_VEH;
LOOP
FETCH LINE_VEH INTO line;
EXIT WHEN LINE_VEH%NOTFOUND;
if line.ALD_PROSYS_OR_ATT_N is null then
v_error := v_error ||'ALD_PROD_SYSTEM|';
end if;
...
update XXALD_INT_IN_VEHICLE_ITEM
set ERROR_CODE= v_error
where current of LINE_VEH;
commit;
END LOOP;
CLOSE LINE_VEH;
The problem is, why after the traitement of only the first line, the loop finishes?
The other 10 lines are not fetched.
Can you move the commit outside the loop?
I think it conflicts with the for update cursor. (for update is only living in one transaction.)
CREATE TABLE EMP AS
SELECT * FROM EMPLOYEES WHERE ROWNUM <=11;
COMMIT;
SELECT * FROM EMP;
DECLARE
CURSOR LINE_VEH
IS
SELECT * FROM EMP FOR UPDATE;
line LINE_VEH%ROWTYPE;
v_error NUMBER;
--I take the lines one by one, and handle it, then update a field of this line(error_code).
BEGIN
OPEN LINE_VEH;
LOOP
FETCH LINE_VEH INTO line;
EXIT WHEN LINE_VEH%NOTFOUND;
IF line.DEPARTMENT_ID =200 THEN
v_error := 300;
END IF;
UPDATE EMP SET DEPARTMENT_ID= v_error WHERE CURRENT OF LINE_VEH;
--COMMIT;
END LOOP;
DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);
END;
/
FOR UPDATE cursor autometically commits. so I have commented out the commit part. This updates all the rows that satisfies the condition.
I'd like a code sample.
I'm tryng this:
DECLARE
var NUMBER;
BEGIN
/*N.B. for loop variables in pl/sql are new declarations, with scope only inside the loop */
FOR var IN 0 .. 10 LOOP
DBMS_OUTPUT.put_line(var);
END LOOP;
IF (var IS NULL) THEN
DBMS_OUTPUT.put_line('var is null');
ELSE
DBMS_OUTPUT.put_line('var is not null');
END IF;
END;
and getting no output (though I know it's not a infinite loop). Why is this one not printing?
edit: The not-printing code was fixed via the database manager interface.
A LOOP without an EXIT statement is one way to generate an infinite loop in PL/SQL
BEGIN
LOOP
null;
END LOOP;
END;
You could also write a WHILE loop that never ends
BEGIN
WHILE( true )
LOOP
NULL;
END LOOP;
END;
If your problem is that you are getting no output, then you may not have enabled DBMS OUTPUT yet. You can do that with:
set serveroutput on
A loop containing a DBMS_OUTPUT.PUT_LINE will not be infinite (if serveroutput is enabled) as, eventually, it will fill the entire output buffer or the available memory. The limit used to be about 1 million bytes so would get hit quite quickly. If it goes to fill up the entire computer memory, that can take quite some time.
On infinite loops, I went through a bad patch of forgetting to go to the next element in a table.
DECLARE
type typ_tab is table of varchar2(10) index by pls_integer;
t_tab typ_tab;
v_ind number;
BEGIN
t_tab(10) := 'A';
t_tab(20) := 'B';
v_ind := t_tab.first;
WHILE v_ind IS NOT NULL LOOP
dbms_output.put_line(t_tab(v_ind));
v_ind := t_tab.next(v_ind); --Forget this and it loops forever
END LOOP;
END;
Once they get into such a loop, the session may need to be killed by the DBA.
Don't know why would you need it, but:
BEGIN
WHILE 1 = 1
LOOP
NULL;
END LOOP;
END;