For loop PLSQL Statement Ignored - oracle

create or replace procedure salarycompall as
CURSOR empIDs IS SELECT employee_id from EMPLOYEES;
begin
FOR e in empIDs
LOOP
SALARYCOMP(e);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
end salarycompall;
This procedure should call another procedure SALARYCOMP. The SALARYCOMP expects the Datatype EMPLOYEES.EMPLOYEE_ID%type, but i get the errors Error(7,9): PL/SQL: Statement ignored and Error(7,9): PLS-00306: wrong number or types of arguments in call to 'SALARYCOMP'.
I have tried just printing out every e in empIDs but i get the same errors, so is there something wrong in my for loop?
Kind Regards

Your cursor loop variable e is a record, not a scalar value; you need to refer to the field within that record:
SALARYCOMP(e.employee_id);
To think about why you can't just pass e, imagine your cursor query was:
SELECT employee_id, manager_id from EMPLOYEES
Then if you called SALARYCOMP(e) you wouldn't just be passing the employee_id it's expecting, you'd be passing both employee_id and manager_id; so then it's more obvious that're you're passing a record comprised of both values, and that you actually need to specify the field you meant, with SALARYCOMP(e.employee_id).
With only one column in your cursor query it's perhaps a bit less obvious - but it's still the same mechanism, and isn't treated any differently. Calling SALARYCOMP(e) still passes the whole record (with a single field).

Related

Trying to insert into a table while looping through pl SQL

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;

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 could I fix error for Stored Procedure Oracle PL/SQL?

I want to solve this:
Create a stored procedure named insert_num_proc that will insert numbers into the FLOAT_SAMPLE table.
This procedure should have two parameters, one for each of the two columns in this table.
The procedure should check the float_id column to ensure there is no duplicate (as unique values are not enforced in this column). If
there is a duplicate float_id, then the row should not be inserted,
instead printing a message saying “Duplicate value in float_id”.
Below is my query. For the second query, I execute my stored procedure to see if it shows my application error message, and it is not working.
CREATE OR REPLACE PROCEDURE insert_num_proc
(
float_id_param float_sample.float_id%TYPE,
float_value_param float_sample.float_value%TYPE)
AS
float_checker float_sample.float_id%TYPE;
BEGIN
SELECT float_id INTO float_checker FROM float_sample;
INSERT INTO float_sample (float_id, float_value)
VALUES
(float_id_param, float_value_param);
IF float_checker = float_id_param THEN
RAISE_APPLICATION_ERROR(-20001, 'Duplicate value is inserted.');
END IF;
END;
/
EXECUTE insert_num_proc(3,2);
Below is error message I get
Error report - ORA-01422: exact fetch returns more than requested
number of rows ORA-06512: at "DL29232.INSERT_NUM_PROC", line 9
ORA-06512: at line 1
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
You are missing a WHERE clause to check if that id exists, so it returns multiple rows which the single static variable float_checker cannot hold.
Another problem is that if you don't have an entry already in the table, your select statement will fail with no_data_found exception.
So, define your float_checker differently so that it will save the count.
Further, your INSERT should come after the IF condition
CREATE OR REPLACE PROCEDURE insert_num_proc (
float_id_param float_sample.float_id%TYPE,
float_value_param float_sample.float_value%TYPE
) AS
float_checker INTEGER := 0;
BEGIN
SELECT COUNT(*)
INTO float_checker
FROM float_sample WHERE
float_id = float_id_param; --where clause for the passed id.
IF float_checker > 0
THEN
raise_application_error(-20001,'Duplicate value is inserted.');
END IF;
INSERT INTO float_sample (
float_id,
float_value
) VALUES (
float_id_param,
float_value_param
);
END;
/
Since this is an exercise, it's ok to use this code. But, in a real time scenario, the 3rd requirement should be handled by a UNIQUE CONSTRAINT on the table.

Bulk collect sql

i made a data_object by myself :
CREATE OR REPLACE TYPE my_object AS OBJECT(
number_type NUMBER,
varchar_type VARCHAR2(20)
)
and then i create type
CREATE OR REPLACE TYPE my_nt IS TABLE OF my_object;
And I want with nested table and this object make a procedure, that will be return number of employyes of some departments. I ve got Two tables: employees and department a this is my code:
DECLARE
enum_dname my_nt := my_nt();
PROCEDURE print_l IS
BEGIN
DBMS_OUTPUT.put_line('---------------------------------------------------------');
FOR i IN 1..enum_dname.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(enum_dname(i));
END LOOP;
END;
BEGIN
SELECT COUNT(emp_id) as number_of, department_name
BULK COLLECT INTO enum_dname
FROM employees e, department d
WHERE e.department_id = d.department_id
GROUP BY department_name;
print_l;
END;
And it show me errors : PLS - 00306: Wrong numbers of argument in call type: PUT_LINE
and PL\SQL : ORA - 00947:not enough values
THANK YOU!
You have two errors. As #SudiptaMondal pointed out (and as here) you can't pass an object to put_line(), you have to pass a single string value - or something which evaluates to a string, which can be concatenated or implicit converted or whatever. So here you might do:
DBMS_OUTPUT.PUT_LINE(enum_dname(i).varchar_type || ': ' || enum_dname(i).number_type);
or however you want to format that output. Using dbms_output for anything except debugging generally not a good idea as you have no control over whether someone using your code has output enabled. But this may be enough for this exercise.
The second problem, causing the ORA-00947, is because your query is trying to bulk collect two scalar variables into a collection of objects. You need to include the object constructor:
SELECT my_object(COUNT(emp_id), department_name)
BULK COLLECT INTO enum_dname
...

concatenate multiple clobs withing Oracle Procedure

I have an Oracle procedure that is going to accept multiple values within a parameter. Part of the procedure will run a select statement putting the results of the parameter in the where clause and placing the concatenated CLOBs into a variable. I am currently using the query below in the procedure but when I run it I get the error below.
If CLOB_ID is not null then
SELECT cast((collect(CLOB_TEXT) )as CLOB )
into v_MessageBody
FROM MESSAGE_CLOB_TABLE
WHERE MESSAGE_ID in CLOB_ID;
End If;
Error: ORA-00932: incosistant datatypes: expected - got CLOB
I also tried writing this using a LISTAGG function but LISTAGG doesnt work with the CLOB values in the MESSAGE_CLOB_TABLE
Any help would be greatly appreciated! I am using Oracle 11g.
If you need to concatenate in PL/SQL simplest variant is to loop through all selected records and append all found records to result:
create or replace function get_message(p_msg_id in number) return CLOB
is
v_MessageBody CLOB;
begin
-- create new instance of temporary CLOB
dbms_lob.createtemporary(v_MessageBody, true);
-- Fill instance with lines
for cMessages in (
select clob_text
from message_clob_table
where message_id = p_msg_id
order by message_row
)
loop
-- add line
dbms_lob.append(v_MessageBody, cMessages.clob_text);
end loop;
-- return collected lines as single CLOB
return v_MessageBody;
end;
Example above works if type of CLOB_TEXT field is CLOB and you need to collect only one message. You can test function in this SQLFiddle.
If you need to select many messages together based on list of his ID's, function becomes a little bit more complex, but principle remains the same.

Resources