PL/SQL No Data found error on forall loop - loops

I am getting no data found error while looping over an array. The execute immediate has data, but the forall loop is giving no data found error and not able to iterate over the collection.
Please find the code below. code_arr.FIRST seems to have some issue. Table has data and executing sql gives data on editor. Could you please help.
create or replace PACKAGE TEST AS
FUNCTION TEST RETURN NUMBER;
END;
create or replace PACKAGE BODY TEST AS
FUNCTION TEST RETURN NUMBER
IS
TYPE typ_varchar IS TABLE OF VARCHAR2 (1000) INDEX BY BINARY_INTEGER;
lv_statement VARCHAR2 (1000);
code_arr typ_varchar;
var1 varchar(1000);
BEGIN
lv_statement := 'SELECT lnm.code FROM employee lnm';
EXECUTE IMMEDIATE lv_statement BULK COLLECT
INTO code_arr;
FORALL ix1 IN code_arr.FIRST .. code_arr.LAST SAVE EXCEPTIONS
SELECT code_arr(ix1) into var1 FROM DUAL;
RETURN 1;
END;
END;
Thanks in advance for your help.
Mathew

FORALL is meant for bulk DML and not for looping through data. The syntax diagram shows this:
To be pedantic, SELECT is a form of DML, although it's usually considered separate from commands that modify objects. That might be why the original code sort of works but throws an error at run time instead of at compile time.
If all you need to do is loop through data, just use a cursor for loop like this. Oracle automatically uses bulk collect for these types of loops:
begin
for employees in
(
SELECT lnm.code FROM employee lnm
) loop
--Do something here.
null;
end loop;
end;
/

Related

"ORA-01008: not all variables are mapped " Confused about this error message. How to enter bind variable as null to initiate the function?

I have a db function that populates data from active directory into a table in the database. It works fine without giving any errors.
Next step is to schedule a job in db so that it is run everyday automatically. When I do that, I get this error: ORA-01008: not all variables are mapped
The code I am using in the PL/SQL block is this:
DECLARE
v_Return VARCHAR2(200);
BEGIN
v_Return := PRE_JOB_FUNCTION();
:v_Return := v_Return;
END;
I think that the issue is that v_Return needs to be Null to begin the execution of this function but I am confused how to do that. Can someone please help?

How to call a oracle stored procedure with RECORD as OUT parameter?

I have a procedure in oracle database, which looks like
create or replace PACKAGE MY_PACKAGE AS
TYPE MY_RECORD IS RECORD
(
first_name abc.first_name%TYPE,
middle_name abc.middle_name%TYPE,
last_name abc.last_name%TYPE
);
TYPE MY_RECORD_REF IS REF CURSOR RETURN MY_RECORD;
PROCEDURE getDetails(
last_name IN OUT VARCHAR2,
V_MY_RECORD_REF OUT MY_RECORD_REF);
END MY_PACKAGE;
I want to call the stored procedure to get the data and display them but unable to do so.
Can someone help?
Thanks in advance.
The record is there to give you a definition of the result set. Use the packaged types to define local variables which you use when calling the procedure.
declare
l_result_Set MY_PACKAGE.MY_RECORD_REF;
l_record MY_PACKAGE.MY_RECORD;
l_name abc.last_name%TYPE ;
begin
-- get the results
MY_PACKAGE.getDetails(
l_name
, l_result_Set );
-- now read them
loop
fetch l_result_Set into l_record;
exit when l_result_Set%notfound;
-- do stuff here
end loop;
end;
"do you know how to do the same in java? "
A Ref Cursor is a JDBC Result Set. Andrej Koelewijn has a brief example in this blog post. Note the use of the Oracle library data type OracleTypes.CURSOR. The full sweep of ResultSet is covered in the JDBC Deve guide. Find out more.

Trigger warning: created with compilation errors

i have this trigger and it says Warning: Trigger created with compilation errors.
can you give me quick advice?
/
CREATE OR REPLACE TRIGGER type_check
BEFORE INSERT ON carrs
FOR EACH ROW
BEGIN
IF :new.weight > 3500
THEN
:new.type := 'nakladne';
ELSE
:new.type := 'osobne';
END IF;
END;
/
edit: i still got the same warning
edit: here is table definition
create table carrs (
id_Car Integer not null,
id_board_unit Integer not null,
id_evc_numbers Integer not null,
id_owner Integer,
weight Integer not null );
here are the errors:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/5 PLS-00049: bad bind variable 'NEW.TYPE'
6/5 PLS-00049: bad bind variable 'NEW.TYPE'
If you're using SQL*Plus, type show errors after getting that warning to display the list of syntax errors that were reported. It's far easier to diagnose problems when you know what they are rather than guessing.
At a minimum, your references to the :new pseudorecord need to include the colon prefix :new rather than new. The assignment operator in PL/SQL is also := not =. So, at a minimum, you'd want something like
CREATE OR REPLACE TRIGGER type_check
BEFORE INSERT ON carrs
FOR EACH ROW
BEGIN
IF :new.weight > 3500
THEN
:new.type := 'nakladne';
ELSE
:new.type := 'osobne';
END IF;
END;
There may be additional errors as well. If there are, type show errors and edit your question to include them.
If you wish to add a new column named type to your table, you would do that before creating your trigger. That's not a particularly good name for a column, though, since type is also a reserved word in Oracle. I'd choose something more meaningful like, say, carr_type. You'd have to specify how large the strings the new column would need to hold when creating it. I'll guess that you want space for 10 characters
ALTER TABLE carrs
ADD( carr_type VARCHAR2(10 CHAR) );
You then probably want to UPDATE your table to populate the new column for your existing data
UPDATE carrs
SET carr_type = (case when weight > 3500
then 'nakladne'
else 'osobne'
end)
before creating your trigger
CREATE OR REPLACE TRIGGER type_check
BEFORE INSERT ON carrs
FOR EACH ROW
BEGIN
IF :new.weight > 3500
THEN
:new.carr_type := 'nakladne';
ELSE
:new.carr_type := 'osobne';
END IF;
END;

How can I copy records between tables only if they are valid according to check constraints in Oracle?

I don't know if that is possible, but I want to copy a bunch of records from a temp table to a normal table. The problem is that some records may violate check constraints so I want to insert everything that is possible and generate error logs somewhere else for the invalid records.
If I execute:
INSERT INTO normal_table
SELECT ... FROM temp_table
nothing would be inserted if any record violates any constraint. I could make a loop and manually insert one by one, but I think the performance would be lower.
Ps: if possible, I'd like a solution that works with Oracle 9
From Oracle 10gR2, you can use the log errors clause:
EXECUTE DBMS_ERRLOG.CREATE_ERROR_LOG('NORMAL_TABLE');
INSERT INTO normal_table
SELECT ... FROM temp_table
LOG ERRORS REJECT LIMIT UNLIMITED;
In its simplest form. You can then see what errors you got:
SELECT ora_err_mesg$
FROM err$_normal_table;
More on the CREATE_ERROR_LOG step here.
I think this approach works from 9i, but don't have an instance available to test on, so this is actually run on 11gR2
Update: tested and tweaked (to avoid PLS-00436) in 9i:
declare
type t_temp_table is table of temp_table%rowtype;
l_temp_table t_temp_table;
l_err_code err_table.err_code%type;
l_err_msg err_table.err_msg%type;
l_id err_table.id%type;
cursor c is select * from temp_table;
error_array exception;
pragma exception_init(error_array, -24381);
begin
open c;
loop
fetch c bulk collect into l_temp_table limit 100;
exit when l_temp_table.count = 0;
begin
forall i in 1..l_temp_table.count save exceptions
insert into normal_table
values l_temp_table(i);
exception
when error_array then
for j in 1..sql%bulk_exceptions.count loop
l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
l_err_code := sql%bulk_exceptions(j).error_code;
l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
insert into err_table(id, err_code, err_msg)
values (l_id, l_err_code, l_err_msg);
end loop;
end;
end loop;
end;
/
With all your real columns instead of just id, which I've done just for demo purposes:
create table normal_table(id number primary key);
create table temp_table(id number);
create table err_table(id number, err_code number, err_msg varchar2(2000));
insert into temp_table values(42);
insert into temp_table values(42);
Then run the anonymous block above...
select * from normal_table;
ID
----------
42
column err_msg format a50
select * from err_table;
ID ERR_CODE ERR_MSG
---------- ---------- --------------------------------------------------
42 1 ORA-00001: unique constraint (.) violated
This is less satisfactory on a few levels - more coding, slower if you have a lot of exceptions (because of the individual inserts for those), doesn't show which constraint was violated (or any other error details), and won't retain the errors if you rollback - though you could call an autonomous transaction to log it if that was an issue, which I doubt here.
If you have a small enough volume of data to not want to worry about the limit clause you can simplify it a bit:
declare
type t_temp_table is table of temp_table%rowtype;
l_temp_table t_temp_table;
l_err_code err_table.err_code%type;
l_err_msg err_table.err_msg%type;
l_id err_table.id%type;
error_array exception;
pragma exception_init(error_array, -24381);
begin
select * bulk collect into l_temp_table from temp_table;
forall i in 1..l_temp_table.count save exceptions
insert into normal_table
values l_temp_table(i);
exception
when error_array then
for j in 1..sql%bulk_exceptions.count loop
l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
l_err_code := sql%bulk_exceptions(j).error_code;
l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
insert into err_table(id, err_code, err_msg)
values (l_id, l_err_code, l_err_msg);
end loop;
end;
/
The 9i documentation doesn't seem to be online any more, but this is in a new-features document, and lots of people have written about it - it's been asked about here before too.
If you're specifically interested only in check constraints then one method to think about is to read the definitions of the target check constraints from the data dictionary and apply them as predicates to the query that extracts data from the source table using dynamic sql.
Given:
create table t1 (
col1 number check (col1 between 3 and 10))
You can:
select constraint_name,
search_condition
from user_constraints
where constraint_type = 'C' and
table_name = 'T1'
The result being:
"SYS_C00226681", "col1 between 3 and 10"
From there it's "a simple matter of coding", as they say, and the method will work on just about any version of Oracle. The most efficient method would probably be to use a multitable insert to direct rows to either the intended target table or to an error logging table based on the result of a CASE statement that applies the check constraint predicates.

JDBC Code Change From SQL Server to Oracle

In the JDBC code, I have the following that is working with SQL Server:
CallableStatement stmt = connection.prepareCall("{ call getName() }");
ResultSet rs = stmt.executeQuery();
if(rs != null)
{
while(rs.next())
{
//do something with rs.getString("name")
}
}
Multiple rows are returned for the above situation.
I understand that the use of a cursor is required to loop through the table in Oracle, but is there any way to keep the above code the same and accomplish the same thing?
Sample PL/SQL code would be much appreciated.
Thanks in advance.
You could implement getName() as a pipelined function:
CREATE OR REPLACE name_record AS OBJECT ( name VARCHAR2(100) );
/
CREATE OR REPLACE name_table AS TABLE OF name_record;
/
CREATE OR REPLACE FUNCTION getName RETURN name_table PIPELINED
AS
n name_record;
BEGIN
-- I have no idea what you're doing here to generate your list of names, so
-- I'll pretend it's a simple query
FOR i IN (SELECT name FROM someTable) LOOP
n := name_record( i.name );
PIPE ROW(n);
END LOOP;
END;
/
You would need to change the actual query in Java to SELECT name FROM TABLE(getName()).
This is straight JDBC, so it'll work with any database that has a valid JDBC driver.
It assumes, of course, that the stored proc exists in both and that you aren't using any non-standard, vendor-proprietary code in your class.

Resources