Oracle: FORALL SAVE EXCEPTIONS not working - oracle

I'm trying to handle a FORALL exceptions with the SAVE EXCEPTIONS clause for the INSERT statement. The process contains two simple tables as per the below;
CREATE TABLE TBL1
("COL1" VARCHAR2(**40** BYTE) NOT NULL);
CREATE TABLE TBL2
("COL1" VARCHAR2(**20** BYTE) NOT NULL);
The only difference here is the size of a column type. Any attempt made to insert a string value of more than 20 characters would result in handling errors.
Here's the script;
DECLARE
TYPE REFCURTYPE IS REF CURSOR;
REFCUR REFCURTYPE;
TYPE ASSARRTYPE IS TABLE OF TBL2%ROWTYPE
INDEX BY PLS_INTEGER;
ASSARR ASSARRTYPE;
DML_ERRORS EXCEPTION;
PRAGMA EXCEPTION_INIT(DML_ERRORS, -24381);
BEGIN
OPEN REFCUR FOR
SELECT
COL1
FROM TBL1;
FETCH REFCUR BULK COLLECT INTO ASSARR;
CLOSE REFCUR;
FORALL i IN ASSARR.FIRST..ASSARR.LAST SAVE EXCEPTIONS
INSERT INTO TBL2
VALUES ASSARR(i);
COMMIT;
EXCEPTION
WHEN DML_ERRORS THEN
FOR j IN 1..SQL%BULK_EXCEPTIONS.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(SQLERRM(-(SQL%BULK_EXCEPTIONS(j).ERROR_CODE)));
END LOOP;
COMMIT;
END;
Somehow, when executing the below block for a data set of more than just one record of 20+ chars, I only receive this single error message;
"ORA-06502: PL/SQL: numeric or value error: Bulk Bind: Truncated Bind"
Thanks in advance for any help.

Related

Table name, column names as argument to stored procedure

I am a newbie to stored procedure and to PL/SQL. There is an existing procedure to copy data from one table to another. I want to rewrite the stored procedure to accept table name and column names as arguments.Did googling on the solution but couldn't come up with a solid solution.
Also planning to add column names as argument so that the column names don't have to be repeatedly added in multiple stored procedures which uses the same tables and columns, helps to reduce maintenance when columns names gets added/removed. Code has been added.
Can anyone help me with this? Any sample code will be very helpful.
create or replace procedure copy_data(startDate DATE, endDate DATE,
mainTable varchar2, subTable varchar2, cpyTbl varchar2)
IS
commit_size NUMBER :=1000;
existing_columns NUMBER;
after_deletion_columns NUMBER;
removed_columns NUMBER;
TYPE order_ids IS TABLE OF subTable.id%TYPE INDEX BY PLS_INTEGER;
removable_order_ids order_ids;
bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT (bulk_errors, -24381);
CURSOR C1 is select id FROM subTable where ord_id in (select ord_id from
mainTable where tmstmp BETWEEN startDate AND endDate);
BEGIN
open C1;
LOOP
FETCH C1 BULK COLLECT INTO removable_order_ids LIMIT commit_size;
forall indx in 1..removable_order_ids.COUNT
INSERT INTO cpyTbl (id, ord_id, name, phon_nbr)
select id, ord_id, name, phon_nbr from subTable
where ord_id = removable_order_ids(indx) LOG ERRORS INTO
ERR$_cpyTbl('INSERT') REJECT LIMIT UNLIMITED;
COMMIT;
EXIT WHEN removable_order_ids.COUNT < commit_size;
END LOOP;
COMMIT;
end;

Using %ROWTYPE in Procedure in Oracle

I am trying to use %ROWTYPE in my code and trying to insert value into it using a cursor for loop as below :
CREATE or REPLACE PROCEDURE test_acr (
PROJECT_START_DATE IN DATE,USER_ID IN VARCHAR2)
IS TYPE acr_new IS TABLE OF acr_projected_new%ROWTYPE
INDEX BY SIMPLE_INTEGER;
acr_projected_neww acr_new;
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE
FROM weekending_table
WHERE WEEKEND_DATE BETWEEN PROJECT_START_DATE AND sysdate;
BEGIN
FOR WEEKEND_DATE_REC in WEEKENDING_DATE LOOP
INSERT INTO acr_projected_neww(WEEKEND_DATE,USERID,TIMESTAMP,ACR_PROJECTED,artificial_id)
SELECT WEEKEND_DATE_REC.WEEKEND_DATE,USER_ID,sysdate,
(select sum(acr_h.activity_impact)
FROM ACR_HISTORY acr_h
LEFT JOIN Activity act on act.activity_id = acr_h.activity_id
LEFT JOIN Activity_Date_Duration act_d on act_d.activity_id = act.activity_id),1 from dual;
END LOOP;
END test_acr;
When i try to run this i get below error:
Error(54,14): PL/SQL: ORA-00942: table or view does not exist
My Requirement is to create virtual table and insert the data into it using cursor for loop if not then any other means is appreciated.
Please help it will be greatly appreciated!
Looks like table name is incorrect in your INSERT statement.
You need not use two queries. Instead, define your cursor such that it has all the columns of the records you want to store in the collection. Then use BULK COLLECT INTO instead of insert as shown. Define your collection as table of cursor%ROWTYPE.
CREATE OR REPLACE PROCEDURE test_acr
IS
CURSOR WEEKENDING_DATE
IS
SELECT a.col1,a.col2,b.col1,b.col2 ,c.col1
from table1 a , table2 b LEFT JOIN table3 c; --Here include all the data from the required tables.
TYPE acr_new
IS
TABLE OF WEEKENDING_DATE%ROWTYPE;
acr_projected_neww acr_new;
BEGIN
FETCH WEEKENDING_DATE BULK COLLECT INTO acr_projected_neww;
END test_acr;
If you need to manipulate your data (access each row - then see script below, this is sequential access (inserts) into nested table (PL/SQL collection)
CREATE or REPLACE PROCEDURE test_acr (PROJECT_START_DATE IN DATE,USER_ID IN VARCHAR2)
IS
TYPE acr_new
IS TABLE OF acr_projected_new%ROWTYPE; // nested table, notice absence of INDEX by clause
acr_projected_neww acr_new := acr_projected_neww(); // instantiation, constructor call
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE
FROM weekending_table
WHERE WEEKEND_DATE BETWEEN PROJECT_START_DATE AND sysdate;
BEGIN
FOR WEEKEND_DATE_REC in WEEKENDING_DATE
LOOP
acr_new.extend; // make room for the next element in collection
acr_new(acr_new.last) := WEEKEND_DATE_REC; // Adding seq. to the end of collection
...
END LOOP;
END test_acr;
However if you want to BULK INSERT (there is no requirement to get access to each row) see script below
CREATE or REPLACE PROCEDURE test_acr (PROJECT_START_DATE IN DATE,USER_ID IN VARCHAR2)
IS
TYPE acr_new IS TABLE OF acr_projected_new%ROWTYPE; // no INDEX BY clause
acr_projected_neww acr_new = acr_new(); // Notice constructor call
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE
FROM weekending_table
WHERE WEEKEND_DATE BETWEEN PROJECT_START_DATE AND sysdate;
BEGIN
FETCH WEEKENDING_DATE BULK COLLECT INTO acr_projected_neww;
...
END LOOP;
END test_acr;
I have used temporary table outside my procedure:
CREATE GLOBAL TEMPORARY TABLE "MY_TEMP"
( "WEEKEND_DATE" DATE,
"USERID" VARCHAR2(255 BYTE),
"TIMESTAMP" TIMESTAMP (6),
"ACR_PROJECTED" NUMBER,
"ARTIFICIAL_ID" NUMBER
) ON COMMIT PRESERVE ROWS ;
i have just used the above temporary table inside my Procedure
create or replace PROCEDURE GET_ACR_TEST(
PROJECT_START_DATE IN DATE ,
USER_ID IN VARCHAR2,
) AS
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE, DURATION
FROM weekending_table where WEEKEND_DATE between PROJECT_START_DATE and sysdate;
Begin
FOR WEEKEND_DATE_REC in WEEKENDING_DATE
LOOP
insert into MY_TEMP (WEEKEND_DATE,USERID,TIMESTAMP,ACR_PROJECTED,artificial_id)
select WEEKEND_DATE_REC.WEEKEND_DATE,USER_ID,sysdate,
(select sum(acr_h.activity_impact)
from ACR_HISTORY acr_h
LEFT JOIN Activity act on act.activity_id = acr_h.activity_id
LEFT JOIN Activity_Date_Duration act_d on act_d.activity_id = act.activity_id),1
from dual;
End Loop;
END GET_ACR_TEST;
The above method is working.
Thank you all for your comments!

PL/SQL - Split the loop to load the bad data

declare
cursor c_data
is
select * from test_product_u;
error_row varchar2(4000);
v_errormsg varchar2(200);
begin
for i in c_data
loop
begin
insert into test_product_u_final (PRODUCT_NO, CREATED_DATE_RAW, DATE_FORMAT)
values (i.PRODUCT_NO, i.CREATED_DATE_RAW,i.DATE_FORMAT);
commit;
exception when others then
error_row := i.PRODUCT_NO ||';'|| i.CREATED_DATE_RAW ||';'|| i.DATE_FORMAT;
v_errormsg := SUBSTR(SQLERRM,1,64);
insert into test_product_error_new(error_no,error_row_msg,errormsg_sql)
values (ERROR_NO.NEXTVAL,error_row,v_errormsg);
end;
end loop;
end;
1 - The above code inserts all the rows irrespective bad or good into the error table. I would want to split the code to just put the good data in the destination and bad data into the error? Any help here.
When I split the loop the reference to the FOR loop is not there.
Sample data
Product_no CREATED_DATE_RAW CREATED_DATE PRICE
1 01-JAN-16 01-JAN-16 55
2 03-JAN-16 03-JAN-16 null
No need for PL/SQL or a loop here. You can use Oracle's error logging feature for this:
First create a table where the errors should be stored:
execute dbms_errlog.create_error_log('TEST_PRODUCT_U_FINAL', 'TEST_PRODUCT_ERRORS');
Then run the insert:
insert into test_product_u_final (PRODUCT_NO, CREATED_DATE_RAW, DATE_FORMAT)
select i.PRODUCT_NO, i.CREATED_DATE_RAW,i.DATE_FORMAT
from test_product_u i
log errors into test_product_errors
reject limit unlimited;
Documentation for dbms_errlog
Documentation for log errors into

PLSQL: BEFORE INSERT TRIGGER (check value in column from other table before allowing insert)

I've made a simple DVD store database. The DVD table has a column "status" which can be either 'FOR_RENT','FOR_SALE','RENTED',or 'SOLD'. I want to write a trigger to block any insertions into my RENTALS table if the status column in the DVD table is not set to 'FOR_RENT'.
Much of the documents I've looked at generally don't show example using values from two different tables so I'm a bit flummaxed.
This is what I believe has been my best attempt so far:
CREATE OR REPLACE TRIGGER RENTAL_UNAVAILABLE
BEFORE INSERT ON RENTAL;
FOR EACH ROW
WHEN (DVD.STATUS != 'FOR_RENT')
DECLARE
dvd_rented EXCEPTION;
PRAGMA EXCEPTION_INIT( dvd_rented, -20001 );
BEGIN
RAISE dvd_rented;
EXCEPTION
WHEN dvd_rented THEN
RAISE_APPLICATION_ERROR(-20001,'DVD has been rented');
END;
/
I'm getting this error:
ORA-00911: invalid character
Try this - I have not complied the code, but should be good. In case you see any compilation issues let me know and post schema on sqlfiddle.com
CREATE OR REPLACE TRIGGER rental_unavailable
BEFORE INSERT
ON rental
FOR EACH ROW
DECLARE
dvd_rented EXCEPTION;
PRAGMA EXCEPTION_INIT (dvd_rented, -20001);
n_count NUMBER (1);
BEGIN
SELECT COUNT (*)
INTO n_count
FROM dvd
WHERE dvd_id = :NEW.dvd_id AND dvd.status = 'FOR_RENT' AND ROWNUM < 2;
IF n_count > 0
THEN
RAISE dvd_rented;
END IF;
EXCEPTION
WHEN dvd_rented
THEN
raise_application_error (-20001, 'DVD has been rented');
END;

dbms_sql.to_cursor_number - Getting Invalid Cursor error for SYS_REFCURSOR

I have the following code for table and view creation.
create table test_company
(
comp_id number
, comp_name varchar2(500)
)
;
insert into test_company values(1, 'CompanyA');
insert into test_company values(2, 'CompanyB');
insert into test_company values(3, 'CompanyC');
create or replace view test_company_view as select * from test_company;
And I have the following code for my cursor testing. But dbms_sql.to_cursor_number got error ORA-01001: invalid cursor
set serveroutput on;
declare
reader test_company_view%ROWTYPE;
datacursor SYS_REFCURSOR;
v_cursor_id number;
begin
open datacursor for select * from test_company_view;
v_cursor_id := dbms_sql.to_cursor_number(datacursor); -- ERROR: invalid cursor
loop fetch datacursor into reader;
exit when datacursor%NOTFOUND;
dbms_output.put_line(reader.comp_id);
end loop;
close datacursor;
end;
What did I do wrong? Thank you for your helps!
I have tried strongly-typed REF CURSOR and weakly-typed REF CURSOR but they got the same error.
From the documentation:
After you convert a REF CURSOR variable to a SQL cursor number, native dynamic SQL operations cannot access it.
It is not dbms_sql.to_cursor_number that is raising the error, it is fetch datacursor into reader since datacursor can no longer be accessed.

Resources