Using cursor after a delete statement in a stored procedure [duplicate] - oracle

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Oracle Accessing updated records during the same transaction
I have an Oracle stored procedure somewhat like this (actual sqls are different)
CREATE OR REPLACE PROCEDURE mysp
IS
v_copied_row table%ROWTYPE ;
CURSOR p_copied_rows IS
select *
from table ;
BEGIN
delete from table
where <some condition>
OPEN p_copied_rows ;
LOOP
FETCH p_copied_rows into v_copied_rows ;
<do something with fetched row>
END LOOP
close p_copied_rows;
END;
Ideally, I am expecting that the deleted rows should not be part of the result set I am fetching from the cursor, but those are.
I am new to oracle, and want to understand if i am doing something wrong here?
P.S. I've to use cursor for some complex things, so replacing cursor with the SQL is not an option.

If your actual code matched the code you posted, the rows you you delete would not be returned by the cursor
If I create a table with 100 rows
SQL> ed
Wrote file afiedt.buf
1 create table foo
2 as
3 select level col1
4 from dual
5* connect by level <= 100
SQL> /
Table created.
and then create a PL/SQL block that replicates what you posted which deletes 98 of the rows, the cursor that is opened will return only 2 rows
SQL> select count(*) from foo;
COUNT(*)
----------
100
SQL> declare
2 cursor non_deleted_rows
3 is select *
4 from foo;
5 l_rec foo%rowtype;
6 begin
7 delete from foo
8 where col1 <= 98;
9
10 open non_deleted_rows;
11 loop
12 fetch non_deleted_rows into l_rec;
13 exit when non_deleted_rows%notfound;
14
15 dbms_output.put_line( l_rec.col1 );
16 end loop;
17 end;
18 /
99
100
PL/SQL procedure successfully completed.
Now, if you open the cursor before you issue the DELETE, the cursor will return the rows that were deleted. Perhaps in your actual code, the OPEN statement is before the DELETE.

Related

PL/SQL procedure with cursor and rowtype

I am working in a games data base. I want to create a procedure which shows the games created between two dates.
I am using a cursor and a rowtype like this:
CREATE OR REPLACE procedure p_games(v_date1 games.date%type, v_date2 games.date%type)
AS
v_games games%rowtype;
CURSOR checkGames IS
SELECT * INTO v_games
FROM games
WHERE date BETWEEN v_date1 AND v_date2;
BEGIN
FOR register IN checkGames LOOP
dbms_output.put_line(register.v_games);
END LOOP;
END;
/
but when I run it the error is
PLS-00302: the component 'V_GAMES' must be declared.
Should I declare it in any other way?
Not exactly like that.
you don't have to declare cursor variable as you're using a cursor FOR loop
you don't select INTO while declaring a cursor; you would FETCH into if you used a different approach (see example below)
Sample table:
SQL> create table games
2 (id number,
3 c_date date
4 );
Table created.
SQL> insert into games (id, c_date) values (1, date '2022-04-25');
1 row created.
Your procedure, slightly modified:
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 BEGIN
9 FOR register IN checkGames LOOP
10 dbms_output.put_line(register.id);
11 END LOOP;
12 END;
13 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>
A different approach; as you can notice, a cursor FOR loop is way simpler as Oracle does most of the dirty job for you (opening the cursor, fetching from it, taking care about exiting the loop, closing the cursor):
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 v_games checkGames%rowtype;
9 BEGIN
10 open checkGames;
11 loop
12 fetch checkGames into v_games;
13 exit when checkGames%notfound;
14
15 dbms_output.put_line(v_games.id);
16 END LOOP;
17 close checkGames;
18 END;
19 /
Procedure created.
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>

declaring pl sql variable using query then using in subsequent query

I’ve read some on declaring and using variables in PL/SQL. and I’ve only done so in other versions (TSQL) or code languages.
Below is what I’m trying to do, which is
to declare a variable
assign a value to that variable via query
use the subsequent result from #2
in another query.
I’ve tried other methods listed on the Internet but nothing I do seems to work when calling the variable in the final query.
The declaration and into statements work but the last select query does not.
declare
Degr_term varchar2(20);
begin
select max(z.term)
INTO degr_term
from dwh.rpt_ersd_vw d
join dwh.dim_ers_term_vw z
on d.DIM_DEGR_ERS_TERM_SKEY = z.dim_ers_term_skey;
select * from ir_wip.stem_enr s where s.min_deg_term = degr_term;
end;
Just like you selected INTO from the 1st select statement, you have to do it in the 2nd as well. It means that you'll have to declare a variable which will hold its contents.
Presuming - maybe not correctly - that 2nd select returns only one row - you could
SQL> declare
2 degr_term varchar2(20);
3 l_stem stem_enr%rowtype; --> this
4 begin
5 select max(z.term)
6 into degr_term
7 from rpt_ersd_vw d
8 join dim_ers_term_vw z
9 on d.DIM_DEGR_ERS_TERM_SKEY = z.dim_ers_term_skey;
10
11 select *
12 into l_stem --> this
13 from stem_enr s
14 where s.min_deg_term = degr_term;
15 end;
16 /
PL/SQL procedure successfully completed.
SQL>
(I removed schema names as I don't have them and didn't feel like creating ones.)
If the 2nd query returns more than a single row, then you'd e.g.
SQL> declare
2 degr_term varchar2(20);
3 type l_stem_typ is table of stem_enr%rowtype;
4 l_stem_tab l_stem_typ;
5 begin
6 select max(z.term)
7 into degr_term
8 from rpt_ersd_vw d
9 join dim_ers_term_vw z
10 on d.DIM_DEGR_ERS_TERM_SKEY = z.dim_ers_term_skey;
11
12 select *
13 bulk collect
14 into l_stem_tab
15 from stem_enr s
16 where s.min_deg_term = degr_term;
17 end;
18 /
PL/SQL procedure successfully completed.
SQL>
[EDIT: How to output the result? Using a loop]
Presume that table looks like this (I don't know really, you never posted any sample data):
SQL> select * from stem_enr;
MIN_DEG_TERM NAME SURN
------------ ------ ----
1 Little Foot
1 Big Foot
Then you'd (simplified example; I didn't feel like creating other tables as well):
SQL> declare
2 type l_stem_typ is table of stem_enr%rowtype;
3 l_stem_tab l_stem_typ;
4 begin
5 select *
6 bulk collect
7 into l_stem_tab
8 from stem_enr s
9 where s.min_deg_term = 1;
10
11 for i in 1 .. l_stem_tab.count loop
12 dbms_output.put_line(l_stem_tab(i).name ||' '|| l_stem_tab(i).surname);
13 end loop;
14 end;
15 /
Little Foot
Big Foot
PL/SQL procedure successfully completed.
SQL>

How do I loop through the value of a field in a table column?

I have a table naprav, where there are fields naprav_id and naprav_name, trying to write something like this:
For naprav_id in (select naprav_id from naprav)
Loop
select naprav_name from narrow where to.napravit=napravit
End loop;
I understand that this code is meaningless, but it is necessary to be able to go through the value of the fields in the loop. How can this be adequately implemented?
If I understood you correctly, that would be something like this:
SQL> declare
2 l_naprav_name naprav.naprav_name%type;
3 begin
4 for cur_r in (select naprav_id from naprav) loop
5 select naprav_name
6 into l_naprav_name
7 from naprav
8 where naprav_id = cur_r.naprav_id;
9
10 dbms_output.put_line(l_naprav_name);
11 end loop;
12 end;
13 /
CLARK
KING
MILLER
PL/SQL procedure successfully completed.
SQL>

PL/SQL error reference to uninitialised collection error even when its initialised

I have a PL/SQL script which used nested table. Below is the sample code.
type rec is record
(
--col data types here
)
type rec_table is table of rec;
v_rec_table rec_table := rec_table(); --initialising here.
arr_size integer := 0; --edit 1
...
...
begin
...
...
open cursor;
loop
fetch cursor bulk collect into v_rec_table limit arr_size; --edit
if nvl(v_rec_table.last,0) > 0 --it shows error is here.
then
...
...
end if;
The error is ORA-06531: Reference to uninitialized collection. Please help me debug this one.
If would help if you posted complete (sample) code (instead of "...").
Here's an example based on Scott's schema which works OK (your error line is line 14):
SQL> declare
2 type rec is record (id number);
3 type rec_table is table of rec;
4 v_rec_table rec_table := rec_table();
5
6 cursor c1 is select empno from emp;
7 begin
8 open c1;
9 loop
10 exit when c1%notfound;
11 fetch c1 bulk collect into v_rec_table;
12 end loop;
13 dbms_output.put_line('last = ' || v_rec_table.last);
14 if nvl(v_rec_table.last, 0) > 0 then
15 dbms_output.put_line('last exists');
16 end if;
17 close c1;
18 end;
19 /
last = 12
last exists
PL/SQL procedure successfully completed.
Though, I'm not sure what's that LOOP used for, as you could have done it as
SQL> declare
2 type rec is record (id number);
3 type rec_table is table of rec;
4 v_rec_table rec_table := rec_table();
5 begin
6 select empno bulk collect into v_rec_table from emp;
7 dbms_output.put_line('last = ' || v_rec_table.last);
8 end;
9 /
last = 12
PL/SQL procedure successfully completed.
Although your problem is not reproducible, there are few things I found could be improved in your code.
If you are using a bulk collect, the collection is initialised automatically and hence, this line is not required
v_rec_table rec_table := rec_table();
Also, as #Littlefoot mentioned, LOOP is not required unless you are using the LIMIT clause.
You may use the COUNT collection function instead of nvl(v_rec_table.last,0)
v_rec_table.count
Instead of defining a record type and bulk collecting into it, you may define a collection of cursor%ROWTYPE;
CURSOR cur is SELECT column1,column2 FROM tablename;
type rec_table is table of cur%ROWTYPE;
v_rec_table rec_table;
Apologies since i did not post whole code. The error occurred because i did not add index by to two columns in the record definition.
After reading through few articles i found my flaw.

Find out if a collection was populated by bulk collect

I created an oracle Object Type like this:
CREATE OR REPLACE TYPE DFBOWNER."RPT_WIRE_IMPORT_ROWTYPE" AS OBJECT
(
REC_VALUE_DATE DATE
)
/
And then a collection based on this type:
CREATE OR REPLACE TYPE DFBOWNER."RPT_WIRE_IMPORT_TABLETYPE" IS TABLE OF RPT_WIRE_IMPORT_RowType;
/
Now I populate the collection using oracle bulk collect into syntax inside a procedure.
So now i want to test if the collection actually got populated, and i am not sure how to do it.
I tried looking it up:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjcol.htm#autoId17 but I am not able to find what I need.
I also have another question. When the procedure bulk collects data into collections, does the data in the collection become permanent as in a table? Or is it semi-permanent...i.e. only lives for the session...as in a temp table.
I suspect you are looking for the COUNT method, i.e.
DECLARE
l_local_collection dbfowner.rpt_wire_import_tabletype;
BEGIN
SELECT sysdate + level
BULK COLLECT INTO l_local_collection
FROM dual
CONNECT BY level <= 10;
dbms_output.put_line( 'l_local_collection contains ' ||
l_local_collection.count ||
' elements.' );
END;
Like any local variable, l_local_collection will have the scope of the block in which it is declared. The data is stored in the PGA for the session. The data in a collection is not permanent.
You can select from the local collection
SQL> create type some_object as object (
2 rec_value_date date
3 );
4 /
Type created.
SQL> create type some_coll
2 as table of some_object;
3 /
Type created.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_local_collection some_coll;
3 begin
4 select some_object( sysdate + numtodsinterval( level, 'day' ) )
5 bulk collect into l_local_collection
6 from dual
7 connect by level <= 10;
8 for x in (select * from table( l_local_collection ))
9 loop
10 dbms_output.put_line( x.rec_value_date );
11 end loop;
12* end;
SQL> /
20-AUG-12
21-AUG-12
22-AUG-12
23-AUG-12
24-AUG-12
25-AUG-12
26-AUG-12
27-AUG-12
28-AUG-12
29-AUG-12
PL/SQL procedure successfully completed.
but it generally doesn't make sense to go through the effort of pulling all the data from the SQL VM into the PL/SQL VM only to then pass all of the data back to the SQL VM in order to issue the SELECT statement. It would generally make more sense to just keep the data in SQL or to define a pipelined table function to return the data.
If you merely want to iterate over the elements in the collection
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_local_collection some_coll;
3 begin
4 select some_object( sysdate + numtodsinterval( level, 'day' ) )
5 bulk collect into l_local_collection
6 from dual
7 connect by level <= 10;
8 for i in 1 .. l_local_collection.count
9 loop
10 dbms_output.put_line( l_local_collection(i).rec_value_date );
11 end loop;
12* end;
SQL> /
20-AUG-12
21-AUG-12
22-AUG-12
23-AUG-12
24-AUG-12
25-AUG-12
26-AUG-12
27-AUG-12
28-AUG-12
29-AUG-12
PL/SQL procedure successfully completed.
It would make much more sense to iterate over the elements in the collection, which keeps everything in PL/SQL, than to SELECT from the collection, which forces all the data back into the SQL VM.

Resources