Why is always cursor already open? - sql

I want to get all messages who version is greater than the input and belongs to the same version group. So I created a cursor to loop through the messageHeader table and get all the rows using a cursor. But I get a cursor already open exception. What am I doing wrong here. I have opened and closed the cursor outside the loop.
create or replace
PROCEDURE ALLOW_SCHEDCALC (scheduleID IN NUMBER, flag OUT NUMBER)
is
messageHeaderIDS Number(20,0);
gasDay timestamp;
headerCount number(20);
cursor mHeaderCurs IS SELECT message_header.ID INTO messageHeaderIDS FROM Message_header
where version_group_id =(select DISTINCT version_group_id from message_header where ID= scheduleID)
AND message_header.version > (select DISTINCT version from message_header where ID = scheduleID);
begin
IF mHeaderCurs %ISOPEN THEN
CLOSE mHeaderCurs ;
END IF;
open mHeaderCurs;
FOR mHeader in mHeaderCurs
Loop
--DBMS_OUTPUT.put_line (mHeader.ID);
SELECT COUNT(*) into headerCount FROM nomination_process_queue where ID=mHeader.ID;
IF headerCount > 0
then
flag:=0;
else
flag:=1;
end if;
end loop;
close mHeaderCurs;
--SELECT VALID_FROM INTO gasDay FROM message_header where ID = scheduleID;
end ALLOW_SCHEDCALC;

When you use the FOR loop over a cursor, e.g. FOR mHeader in mHeaderCurs, the Oracle PL/SQL engine handles the cursor open/close for you.
You don't need to open or close the cursor at all.
P.S. this code never made sense in the first place, since this is at the start of the procedure so by definition the cursor cannot be open:
begin
IF mHeaderCurs %ISOPEN THEN
CLOSE mHeaderCurs ;
END IF;

Try this code it may help you :-
create or replace PROCEDURE ALLOW_SCHEDCALC (scheduleID IN NUMBER, flag OUT NUMBER)
is
messageHeaderIDS Number(20,0);
gasDay timestamp;
headerCount number(20);
cursor mHeaderCurs IS SELECT message_header.ID INTO messageHeaderIDS
FROM Message_header
where version_group_id =(select DISTINCT version_group_id from message_header
where ID= scheduleID)
AND message_header.version >
(select DISTINCT version from message_header where ID = scheduleID);
begin
FOR mHeader in mHeaderCurs loop exit when mHeaderCurs%notfound;
--DBMS_OUTPUT.put_line (mHeader.ID);
SELECT COUNT(*) into headerCount FROM nomination_process_queue where ID=mHeader.ID;
IF headerCount > 0
then
flag:=0;
else
flag:=1;
end if;
end loop;
--SELECT VALID_FROM INTO gasDay FROM message_header where ID = scheduleID;
end ALLOW_SCHEDCALC;/

Related

How do I use select in loop continue when statement?

I want to continue a oracle sql loop when I find more then one result in a query - so my simplified code:
declare
cursor foo_cursor select * from foo_table;
foo foo_cursor%ROWTYPE;
begin
open foo_cursor;
loop
fetch foo_cursor into foo;
exit when foo_cursor%NOTFOUND;
continue when ( -- the next query has entries or an entry,
-- but how do I do this?
select count(*) from bar_table where bar_column=foo.foo_column
group by bar_column having count(1)>1;
)
insert into uninterresting_table (some_column) VALUES
(foo.foo_column);
end loop;
close foo_cursor;
end;
It looks like you only want to act on the records in FOO_TABLE when at least two related records exists in BAR_TABLE. You can alter the definition of foo_cursor to take that requirement into account as shown below. That way you don't need to iteratively check for the existence of a record in BAR_TABLE each time.
declare
cursor foo_cursor is
select *
from foo_table foo
where exists (select 1 from bar_table bar
where bar.bar_column = foo.foo_column
having count(*) > 1);
foo foo_cursor%ROWTYPE;
begin
open foo_cursor;
loop
fetch foo_cursor into foo;
exit when foo_cursor%NOTFOUND;
insert into uninteresting_table (some_column) VALUES
(foo.foo_column);
end loop;
close foo_cursor;
end;
/
On the other hand if you are looking to skip records in FOO_TABLE that already have two or more records in BAR_TABLE, you can just invert the existence check and all else would be the same:
declare
cursor foo_cursor is
select *
from foo_table foo
where NOT exists (select 1 from bar_table bar
where bar.bar_column = foo.foo_column
having count(*) > 1);
foo foo_cursor%ROWTYPE;
begin
open foo_cursor;
loop
fetch foo_cursor into foo;
exit when foo_cursor%NOTFOUND;
insert into uninteresting_table (some_column) VALUES
(foo.foo_column);
end loop;
close foo_cursor;
end;
/
If you want to process all records in FOO_TABLE but do additional actions when two or more records exist in BAR_TABLE, you can still do that with a change to your foo_cursor:
declare
cursor foo_cursor is
select foo.*
, case when exists (select 1 from bar_table bar
where bar.bar_column = foo.foo_column
having count(*) > 1)
then 'Y'
else 'N'
end has_two_or_more
from foo_table foo;
foo foo_cursor%ROWTYPE;
begin
open foo_cursor;
loop
fetch foo_cursor into foo;
exit when foo_cursor%NOTFOUND;
continue when foo.has_two_or_more = 'Y';
insert into uninteresting_table (some_column) VALUES
(foo.foo_column);
end loop;
close foo_cursor;
end;
/
if the [select has ... one and more entries] i want go into the next iteration else do more stuff in this iteration.
If there are no records in bar_table you want to do some more processing otherwise you want to skip the processing. There's a way to do that: goto.
Oh yes :)
declare
cursor foo_cursor select * from foo_table;
foo foo_cursor%ROWTYPE;
n pls_integer;
begin
open foo_cursor;
loop
fetch foo_cursor into foo;
exit when foo_cursor%NOTFOUND;
select count(*) into n
from bar_table
where bar_column=foo.foo_column
group by bar_column having count(1)>1;
if n > 0 then
goto skip_point;
end if;
insert into uninterresting_table (some_column) VALUES
(foo.foo_column);
<< skip_point >>
end loop;
close foo_cursor;
end;
Obviously you could just put the whole skippable section into a branch of an IF .. ELSE statement, but where's the fun in that?
So thanks to #APC and #Sentinel -- I still made my version work, but your answers gave me the right dirrections:
declare
cursor foo_cursor select * from foo_table;
foo foo_cursor%ROWTYPE;
n pls_integer
begin
open foo_cursor;
loop
fetch foo_cursor into foo;
exit when foo_cursor%NOTFOUND;
begin
select count(1) into n from bar_table where bar_column=foo.foo_column
group by bar_column;
exception when NO_DATA_FOUND then continue;
end
continue when (n>1);
insert into uninterresting_table (some_column) VALUES
(foo.foo_column);
-- do some more stuff
end loop;
close foo_cursor;
end;
```

Making procedure to insert/update qty. What am I doing wrong?

I am trying to make procedure that count total slab # of from each pallet from prod_result table and insert or update that count to qty column in plt_result table that has same pallet #(plt_no)
I've tried insert, update both but doesn't work.
create or replace PROCEDURE update_qty
is
v_plt_no nvarchar2(20);
v_qty number;
cursor q1 is
select count(slab_no)
into v_qty
from prod_result
where plt_no = v_plt_no;
begin
if v_qty > 0 then
update plt_result
set qty = 'v_qty'
where plt_no = v_plt_no;
end if;
end;
What am I doing wrong here? Help please.
I'm not sure which version you're looking for - is it the one that loops through all PLT_NOs in a table, or the one that should accept PLT_NO as a parameter. Here are both options, see whether any of them helps.
The first one uses cursor FOR loop; it is easier to maintain as Oracle does most of the dirty job for you (opening the cursor, fetching, exiting the loop, closing the cursor).
create or replace procedure update_qty
is
begin
for cur_r in (select plt_no, count(slab_no) v_qty
from prod_result
group by plt_no
)
loop
if cur_r.v_qty > 0 then
update plt_result r set
r.qty = cur_r.v_qty
where r.plt_no = cur_r.plt_no;
end if;
end loop;
end;
The second one accepts PLT_NO as a parameter:
create or replace procedure update_qty
(par_plt_no in prod_result.plt_no%type)
is
v_qty number;
begin
select count(p.slab_no)
into v_qty
from prod_result p
where p.plt_no = par_plt_no;
if v_qty > 0 then
update plt_result r set
r.qty = v_qty
where r.plt_no = par_plt_no;
end if;
end;
Something like the following, where you actually open and fetch from the cursor. You'll have to assign a value to v_plt_no for the cursor to fetch anything.
create or replace PROCEDURE update_qty
is
v_plt_no nvarchar2(20);
v_qty number;
cursor q1 is
select count(slab_no)
into v_qty
from prod_result
where plt_no = v_plt_no;
begin
OPEN q1;
FETCH q1 INTO v_qty;
CLOSE q1;
if v_qty > 0 then
update plt_result
set qty = 'v_qty'
where plt_no = v_plt_no;
end if;
end;

Dynamic SQL - ORACLE

I have the following procedure, which does not compile correctly, because it refers to non existing objects (table does not exist)
Here is only a section of the code (i used generic names for tables and columns):
DECLARE
C INTEGER := 0;
BEGIN
SELECT COUNT(1) INTO C FROM USER_TABLES WHERE TABLE_NAME = 'MY_TABLE';
IF C > 0 THEN
DECLARE
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE = 1;
BEGIN
FOR prec IN c_maps LOOP
some code...;
END LOOP;
EXECUTE IMMEDIATE 'some code..';
END;
END IF;
END;
/
I don't know how to write this statement dynamically, since the table "MY_TABLE" does not exist:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE =1;
I also tried to write it like:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM (Select 'MY_TABLE' from dual) WHERE ACTIVE = 1;
However, than it refers to the column "ACTIVE" which also does not exist at compile time...It is possible to write the whole procedure inside "execute immediate" - block? I have tried different variants, however without success
You may need to open the cursor in a different way, so that the non existing table is only referred in dynamic SQL; for example:
declare
c integer := 0;
curs sys_refcursor;
v1 number;
v2 number;
begin
select count(1)
into c
from user_tables
where table_name = 'MY_TABLE';
if c > 0
then
open curs for 'select column_name1, column_name2 from my_table where active = 1';
loop
fetch curs into v1, v2;
exit when curs%NOTFOUND;
dbms_output.put_line(v1 || ' - ' || v2);
end loop;
else
dbms_output.put_line('The table does not exist');
end if;
end;
/

PL/SQL: ORA-00904: : invalid identifier

I am running the following SP but getting the error c1.pyid is invalid identifier. I am trying to use two different query results from one cursor. If there is any other way of using IF-else clause in a cursor, i am open to that too.
CREATE OR REPLACE
PROCEDURE FIX_DOCUMENT_RECORDS ( i_flag in varchar)
AS
Op_ID VARCHAR(8);
Op_Name VARCHAR(32);
skill VARCHAR(32);
temp_count VARCHAR(8);
temp_status VARCHAR(8):='Submitted';
QRYSTR VARCHAR2(400);
TYPE REF_CUR IS REF CURSOR;
c1 REF_CUR;
BEGIN
IF (i_flag='1') THEN
QRYSTR:='SELECT *
FROM dims_doc_master
WHERE concat_prod_id IS NULL
OR documenttypeid IS NULL
AND (pystatuswork = temp_status);';
ELSE
QRYSTR:='SELECT *
FROM dims_doc_master
WHERE (documentimageid IS NULL
AND p8id IS NULL)
AND (pystatuswork = temp_status);';
END IF;
open c1 FOR QRYSTR;
LOOP
BEGIN
DBMS_OUTPUT.PUT_LINE('loop begin');
UPDATE DIMS_DOC_MASTER
SET pystatuswork ='Cancelled',
documentstatus ='Cancelled',
cancellationdate='31-JAN-14',
cancelledbysid = c1.pxcreateoperator,
cancelreason ='Cancelled due to corruption.'
WHERE pyid =c1.pyid;
DBMS_OUTPUT.PUT_LINE('After updation'||c1.pyid );
--Begin PC_DOCUMENT UPDATION
UPDATE PC_DOCUMENT
SET pystatuswork ='Cancelled',
cancellationdate='31-JAN-14'
WHERE pyid =c1.pyid;
--Begin insert into History
--Select Operator name and ID
SELECT skill
INTO skill
FROM operator_map_skill
WHERE pyuseridentifier=c1.pxcreateoperator
AND rownum =1;
INSERT
INTO DIMS_DOC_HIST
(
DIMS_DOC_ID,
DOC_CHG_USR,
DOC_CHG_DT,
DOC_NEW_STS,
DOC_CHG_CMNT,
CRE_TS,
ROLE,
RSN_DESC,
TARGETROLE,
DOC_CHG_USR_ID,
DOC_ASG_USR_ID,
DOC_ASG_USR,
PREVSTATUS,
PREVSTATUSDT,
ASSIGNEDTODT,
TODISPLAY,
ACTIVITY_NAME
)
VALUES
(
c1.pyid,
'DIMS',
systimestamp,
'Cancelled',
'Cancelled due to corruption',
'31-JAN-14',
skill,
NULL,
skill,
c1.pxcreateoperator,
c1.pxcreateoperator,
c1.pxcreateopname,
'Submitted',
NULL,
systimestamp,
'Y',
'Updation through Script'
);
dbms_output.put_line
(
'Document ID= '||c1.pyid
)
;
SELECT COUNT(*)
INTO temp_count
FROM PC_ASSIGN_WORKBASKET
WHERE pxrefobjectinsname=c1.pyid;
IF(temp_count IS NOT NULL) THEN
DELETE FROM PC_ASSIGN_WORKBASKET WHERE pxrefobjectinsname=c1.pyid;
ELSE
DELETE FROM PC_ASSIGN_WORKLIST WHERE pxrefobjectinsname=c1.pyid;
END IF;
COMMIT;
END;
END LOOP;
CLOSE c1;
END;
You seem confusing cursor and fetched row.
In your current procedure: you open a cursor, do a loop (which looks to be endless since there is no EXIT statement), and after the loop you close the cursor (but it looks it will never happen)
To fetch results from a cursor, do the following:
CREATE OR REPLACE PROCEDURE ...
...
c1 REF_CUR;
ddm_record dims_doc_master%rowtype;
BEGIN
...
OPEN c1;
LOOP
FETCH c1 INTO ddm_record;
EXIT WHEN c1%NOTFOUND;
...
DBMS_OUTPUT.PUT_LINE('Document ID= ' || ddm_record.pyid); -- not c1.pyid
END LOOP;
CLOSE c1;
END;
/
Inspired from examples here: http://plsql-tutorial.com/plsql-explicit-cursors.htm
Try embedding the flag in your where clause:
open c1 FOR
SELECT *
FROM dims_doc_master
WHERE (i_flag='1' AND
(concat_prod_id IS NULL
OR documenttypeid IS NULL
AND (pystatuswork = temp_status))
OR (i_flag<>'1' AND
(documentimageid IS NULL
AND p8id IS NULL)
AND (pystatuswork = temp_status));
The logic can probably be simplified but logically that would work.

INSERT and UPDATE a record using cursors in oracle

I have 2 tables- student and studLoad both having 2 fields studID and studName. I want to load data from student table into stuLoad table.
If the data already exists in the studLoad table, then it should be updated else it should be inserted. following is my code to do so:
create or replace procedure studentLoad is
v_id student.studID%type;
v_name student.studName%type;
v_sn studLoad.studName%type;
cursor cur_load is
select * from student;
begin
open cur_load;
loop
fetch cur_load into v_id,v_name;
exit when cur_load%notfound;
select studName into v_sn from studLoad where studID = v_id;
if(v_sn!= v_name) then
update studLoad set studName= v_name where studID= v_id;
else
insert into studLoad values(v_id,v_name);
dbms_output.put_line(v_id || ' ' || v_name);
end if;
end loop;
close cur_load;
end;
It's not working. the rows in studLoad table are noT updated. How do I solve this? In SQL server we use IF EXISTS(select...from stuLoad..) to check if the record exists in the table, is there a way to do the same in Oracle? if yes then please let me know the same.
This is a highly inefficient way of doing it. You can use the merge statement and then there's no need for cursors, looping or (if you can do without) PL/SQL.
MERGE INTO studLoad l
USING ( SELECT studId, studName FROM student ) s
ON (l.studId = s.studId)
WHEN MATCHED THEN
UPDATE SET l.studName = s.studName
WHERE l.studName != s.studName
WHEN NOT MATCHED THEN
INSERT (l.studID, l.studName)
VALUES (s.studId, s.studName)
Make sure you commit, once completed, in order to be able to see this in the database.
To actually answer your question I would do it something like as follows. This has the benefit of doing most of the work in SQL and only updating based on the rowid, a unique address in the table.
It declares a type, which you place the data within in bulk, 10,000 rows at a time. Then processes these rows individually.
However, as I say this will not be as efficient as merge.
declare
cursor c_data is
select b.rowid as rid, a.studId, a.studName
from student a
left outer join studLoad b
on a.studId = b.studId
and a.studName <> b.studName
;
type t__data is table of c_data%rowtype index by binary_integer;
t_data t__data;
begin
open c_data;
loop
fetch c_data bulk collect into t_data limit 10000;
exit when t_data.count = 0;
for idx in t_data.first .. t_data.last loop
if t_data(idx).rid is null then
insert into studLoad (studId, studName)
values (t_data(idx).studId, t_data(idx).studName);
else
update studLoad
set studName = t_data(idx).studName
where rowid = t_data(idx).rid
;
end if;
end loop;
end loop;
close c_data;
end;
/
If you would like to use your procedure, consider to change some lines:
create or replace procedure studentLoad is
v_id student.studID%type;
v_name student.studName%type;
v_sn studLoad.studName%type;
cursor cur_load is
select * from student;
begin
open cur_load;
loop
fetch cur_load into v_id,v_name;
exit when cur_load%notfound;
begin
select studName into v_sn from studLoad where studID = v_id;
if(v_sn!= v_name) then
update studLoad set studName= v_name where studID= v_id;
end if;
exception
when no_data_found then
insert into studLoad values(v_id,v_name);
end;
dbms_output.put_line(v_id || ' ' || v_name);
end loop;
close cur_load;
end;
I think it should work, didn't test it.