Trying to insert into a table while looping through pl SQL - oracle

I have a view that I need to loop through with all the inventory ids from another table. I keep receiving the following error: Error report -
ORA-06550: line 31, column 31:
PLS-00357: Table,View Or Sequence reference 'MISSINGINVENTORY' not allowed in this context
ORA-06550: line 31, column 2:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
This is my current code I am not sure what the problem would be? I ma fairly new to pl sql
DECLARE
CURSOR inventory_ids_c IS
SELECT DISTINCT fzrfbth_status status,
fzbfbth_inventory_id id
FROM fzbfbth#develop_sw,
fzrfbth#develop_sw
WHERE fzbfbth_inventory_id = fzrfbth_inventory_id
ORDER BY fzrfbth_status,
fzbfbth_inventory_id;
inventory_ids_rec inventory_ids_c%ROWTYPE;
BEGIN
OPEN inventory_ids_c;
FETCH inventory_ids_c INTO inventory_ids_rec;
LOOP
EXIT WHEN inventory_ids_c%NOTFOUND;
fzkfims.P_set_inventory_id#develop_sw(inventory_ids_rec.id);
DECLARE
CURSOR inventory_items IS
SELECT *
FROM fzvfims#develop_sw;
BEGIN
OPEN inventory_items;
LOOP
FETCH inventory_items INTO Missinginventory(last_inventory_date,
atype_title
,
owner, orgn_code, orgn_title, locn_code, room, bldg, sort_room,
ptag
,
manufacturer, model, serial_num, description, custodian, po,
acq_date,
amount,
ownership, schev_year, tag_type, inventory_id, condition,
asset_type
);
EXIT WHEN inventory_items%NOTFOUND;
END LOOP;
CLOSE inventory_items;
END;
END LOOP;
END;

It seems that you are trying to fetch from cursor directly into Missinginventory table (if it is a table). Well, that won't work. First fetch into cursor variables, then insert those variables into a table.
I don't know what this:
fzkfims.P_set_inventory_id#develop_sw(inventory_ids_rec.id);
does; looks like some procedure call. I guess it sets some kind of an "environment", but I don't know how it reflects to code you wrote.
Anyway: why do you use cursor loops? Can't you directly insert data into the table? That would be faster and - hopefully - simpler.
Also, if you declare something, do that at the same place, at the beginning of that PL/SQL procedure. If you scatter declarations all over your code, it is difficult to maintain it. Furthermore, see whether cursor FOR loops are an option as they are simpler to use - Oracle does a lot of things for you. How? Although you still have to write cursor's SELECT statement, you don't have to declare cursor variables, open cursor, fetch, take care about exiting the loop, close the cursor. I'll try to rewrite your code, have a look (as you can see, no declare section at all!).
begin
for cur_1 in (select distinct fzrfbth_status status,
fzbfbth_inventory_id id
from fzbfbth#develop_sw
join fzrfbth#develop_sw
on fzbfbth_inventory_id = fzrfbth_inventory_id
order by fzrfbth_status,
fzbfbth_inventory_id
)
loop
fzkfims.p_set_inventory_id#develop_sw(cur_1.id);
for cur_2 in (select *
from fzvfims#develop_sw
)
loop
-- I shortened it to just a few columns
insert into missinginventory
(last_inventory_date,
atype_title,
asset_type)
values (cur_2.last_inventory_date,
cur_2.atype_title,
cur_2.asset_type);
end loop;
end loop;
end;

Related

Oracle error when selecting into temp table

My ultimate goal is far more complex than this, but this is the cut down version of what is causing my error. I want to put some rows into a temporary table (actually several temp tables, but I can't get by this first hurdle). Here is my PL/SQL;
DECLARE
type L1_store is table of MyTable%rowtype;
L1 L1_store;
BEGIN
select
* bulk collect
into L1
from MyTable
WHERE 1=1
and length(MyColumn1) = 2;
select
L1.MyColumn1
,L1.MyColumn2
from L1;
END;
And here is the error I get;
ORA-06550: line 19, column 6:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 16, column 1:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
The line numbers may be incorrect as I have edited the actual PL/SQL for clarity
****EDIT****
OK, so I originally accepted the answer offered below as it looks like it answers my question, and I can see how I could use it. However, for clarity, here is my ultimate goal, in case there is a better answer than the one I have in my head.
If I was just doing this in SQL I would do something like;
with L1 as
(select * from table),
L2 as
(select * from anothertable)
select L1.Column
from L1
left join L2 on L1.somecolumn = L2.somecolumn
I don't know if this helps or hinders, but thanks all in anticipation of your continued patience.
type L1_store is table of MyTable%rowtype; is not a temporary table; it is a collection data type declared in the PL/SQL scope and cannot be used in the SQL scope. Similarly, %ROWTYPE is a PL/SQL construct.
If you want to use a collection in SQL then declare it in the SQL scope:
CREATE TYPE mytable_data is OBJECT (
mycolumn1 VARCHAR2(50),
mycolumn2 NUMBER,
mycolumn3 DATE
);
CREATE TYPE mytable_data_table IS TABLE OF mytable_data;
so for some test data:
CREATE TABLE MyTable(
mycolumn1 VARCHAR2(50),
mycolumn2 NUMBER,
mycolumn3 DATE
);
INSERT INTO MyTable VALUES ( 'AA', 42, SYSDATE );
Then you can do:
DECLARE
L1 mytable_data_table;
BEGIN
select mytable_data( mycolumn1, mycolumn2, mycolumn3 )
bulk collect into L1
from MyTable
WHERE length(MyColumn1) = 2;
FOR i IN 1 .. L1.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( L1(i).mycolumn1 || ' ' || l1(i).mycolumn2 );
END LOOP;
END;
/
Which outputs:
AA 42
db<>fiddle here
The problem here seems to be the second select in the block. If you're trying to select the data from the record, I would be inclined to put it into a FOR loop. I.e, something like this:
for i in 1 .. L1.count() LOOP
dbms_output.put_line (L1(i).MyColumn1);
end loop;
You could assign the value of MYColumn1 to a variable or use it in some other way.

cannot mix between single row and multi-row (BULK) in INTO list.ERROR in proc

CREATE OR REPLACE PROCEDURE RDBSTAGE.ATCHMNT_ERR_FILEID AUTHID CURRENT_USER
IS
CURSOR cv_atchtab IS
SELECT * FROM ATTACHMENT_ERROR;
I_ATCHMNT_ERR cv_atchtab%ROWTYPE;
V_FILE_ID VARCHAR2(40);
BEGIN
OPEN cv_atchtab;
LOOP
FETCH cv_atchtab BULK COLLECT INTO I_ATCHMNT_ERR;
EXIT WHEN cv_atchtab%NOTFOUND;
FOR i IN 1..I_ATCHMNT_ERR.COUNT
LOOP
SELECT FILE_ID BULK COLLECT
INTO V_FILE_ID
FROM ATTACHMENT_CLAIM t1
WHERE t1.CLAIM_TCN_ID=I_ATCHMNT_ERR(i).CLAIM_TCN_ID;
UPDATE ATTACHMENT_ERROR
SET FILE_ID = V_FILE_ID
WHERE t1.CLAIM_TCN_ID=I_ATCHMNT_ERR.CLAIM_TCN_ID;
END LOOP;
END LOOP;
CLOSE cv_atchtab;
END;
END ATCHMNT_ERR_FILEID;
/
SHOW ERRORS
Procedure ATCHMNT_ERR_FILEID compiled
Errors: check compiler log Errors for PROCEDURE
RDBSTAGE.ATCHMNT_ERR_FILEID:
LINE/COL ERROR
11/40 PLS-00497: cannot mix between single row and multi-row (BULK) in INTO list
14/5 PL/SQL: Statement ignored
14/31 PLS-00302: component 'COUNT' must be declared
PLS-00497: cannot mix between single row and multi-row (BULK) in INTO list
BULK COLLECT INTO is the syntax for populating a PL/SQL collection from a query. But your code is populating a scalar single row variable.
14/31 PLS-00302: component 'COUNT' must be declared
I_ATCHMNT_ERR.COUNT is invalid because count() only applies to collections I_ATCHMNT_ERR is scalar.
To fix this you need to define and use collection types. Something like this:
CREATE OR REPLACE PROCEDURE ATCHMNT_ERR_FILEID
IS
CURSOR cv_atchtab IS
SELECT * FROM ATTACHMENT_ERROR;
type ATCHMNT_ERR_nt is table of cv_atchtab%ROWTYPE;
I_ATCHMNT_ERR ATCHMNT_ERR_nt;
V_FILE_ID VARCHAR2(40);
BEGIN
OPEN cv_atchtab;
LOOP
FETCH cv_atchtab BULK COLLECT INTO I_ATCHMNT_ERR; -- collection type
EXIT WHEN I_ATCHMNT_ERR.COUNT = 0; -- changed this
FOR i IN 1..I_ATCHMNT_ERR.COUNT
LOOP
SELECT FILE_ID
INTO V_FILE_ID -- scalar type
FROM ATTACHMENT_CLAIM t1
WHERE t1.CLAIM_TCN_ID = I_ATCHMNT_ERR(i).CLAIM_TCN_ID;
UPDATE ATTACHMENT_ERROR t2 -- changed this
SET FILE_ID = V_FILE_ID
WHERE t2.CLAIM_TCN_ID = I_ATCHMNT_ERR(i).CLAIM_TCN_ID; -- changed this
END LOOP;
END LOOP;
CLOSE cv_atchtab;
END ATCHMNT_ERR_FILEID;
Here is a db<>fiddle demo of the above working against my guess of the data model.
The Oracle documentation is comprehensive, online and free. The PL/SQL Guide has a whole chapter on Collections and Records which I suggest you read. Find it here.
As an aside, nested loops with single row statements like this are usually a red flag in PL/SQL. They are pretty inefficient and slow. SQL is a set-based language, and we should always try to solve problems using SQL whenever possible, and ideally in one set-based statement. If this code is intended for production (rather than being a homework assignment) you should definitely consider re-writing it in a more performative fashion.

How to have my Oracle PL/SQL block update all entries related to the row_ids referenced by my cursor

I currently have a Oracle stored procedure that is taking data from tables. The problem is I am aggregating /grouping , and I don't want to grab the IDs otherwise that will throw the grouping off. I want to update a column called 'correlated_flag_id' to '1' (done) in the value table after ive inserted the aggregated/grouped result set. I only want to grab IDs that are correlated to the
values that my first cursor grabbed to derive the results. Below is my attempt (which I don't think is correct):
Create or Replace PROCEDURE PROC is
CURSOR c1 is
select sum(v.value_tx) as sum_of_values
, max(v.create_dt) as latest_create_dt
, v.data_date
from value v
group by v.data_date, max(v.create_dt)
BEGIN
Open c1;
LOOP
Fetch c1 into l_var;
insert into value (value_id, value_tx, create_dt, data_date)
values (null, l_var.sum_of_values, l_var.latest_create_dt, l_var.data_Date);
END LOOP;
Close c1;
commit;
--- the bottom is not correct, but i've reached a roadblock
Update value
set correlated_flag_id = 777
where value_id in (select v.value_id from value where trunc(create_dt) <> trunc(sysdate)) (???));
commit;
END PROC;
Thanks in advance and please let me know if there is any more details that I need to provide.
cursor's select is kind of wrong; why are you grouping by a MAX function? It isn't allowed here
switch to cursor FOR loop as it is easier to maintain. You don't have to open a cursor, fetch, exit the loop (which you did not do at all), close the cursor
I'm not sure what VALUE_930 table is doing here, you never mentioned it
your words say "update correlated ID to 1", while code says "update it to 777"
don't commit in a procedure; let caller decide whether it should be done
I'd suggest you to either use a tool which offers code formatting, or format it yourself. Your procedure is not a result of a flood, so don't treat it that way
Finally, here's a suggestion which might (or might not) work as we don't have your tables nor data, but - at least - looks decent.
create or replace procedure proc is
begin
for cur_r in (select v.data_date,
sum(v.value_tx) as sum_of_values,
max(v.create_dt) as latest_create_dt
from value v
group by v.data_date)
loop
insert into value (value_id, value_tx, create_dt, data_date)
values (null, cur_r.sum_of_values, cur_r.latest_create_dt, cur_r.data_date);
update value set
correlated_flag_id = 777
where data_date = cur_r.data_date;
end loop;
end proc;
/

Select Statement in oracle procedure

I can't able to create a select statement in oracle procedure. Please help me to create this.
Now I create the insert,update.delete statement in a procedure but i can't create a select statement. Please help me to create the select statement using cursor.
c_dbuser OUT SYS_REFCURSOR
ELSIF (TYPE_ =1) THEN
OPEN c_dbuser FOR
SELECT * FROM tbl_discount_master ;
CLOSE c_dbuser;
END IF;
call procedure_name(xx,xx,xx,1);
how can i get the selected value using call procedure statement.
In addition to the other suggestion, you have this solution when you are getting exactly one row.
DECLARE
myvar1 mytable.mycolumn1%TYPE;
myvar2 mytable.mycolumn2%TYPE;
BEGIN
SELECT mycolumn1, mycolumn2
INTO myvar1, myvar2
FROM mytable
WHERE …;
END;
This will throw an exception if there is no selected row (NO_DATA_FOUND) or if there is more than one row (TOO_MANY_ROWS).
The difference between select and the insert/update/delete is that you need to select into some structure, either one or more variables or a rowtype variable.
Avoid explicit cursors whenever possible in favour of the faster, less verbose and less error prone implicit cursor.
eg.
for cur_my_query in
select column1,
column2,
...
from ...
where ...
loop
refer here to cur_my_query or my_query.column1 etc
end loop

How to locate the accurate postion in pl/sql promptly (ORA-06502: PL/SQL)

I have a select statement which needs to select dozens of column into self-defined variable in my pl/sql. Like as below:
select col1,
col2,
....
col30
into var1,
...
var30
from table
where ....
While executing the SP I encounter the error:
ORA-06502: PL/SQL: numeric or value error: character string buffer too
small
The error information only points out the first line number of select statement. Even if i can figure out that my defined variable is too small to hold the column, it still makes me hard to locate the error-defined variable precisely. This is not an efficient way for me to debug this sp.
Is there any better idea, please advise me.
Two options are typically used in pl/sql:
1.Define your variables in PL/SQL to match the table's definition, using %type.
define
v_col1 my_table.col1%type;
v_col2 my_table.col2%type;
begin
select col1,col2
into v_col1, v_col2
from my_table
-- some condition that pulls 1 row
where rownum = 1;
end;
2.Define a row variable, using %rowtype
define
v_my_table_row my_table%rowtype;
begin
select *
into v_my_table_row
from my_table
where rownum = 1;
end;

Resources