PL/SQL Bulk Collection Insert syntax - plsql

I am tasked with inserting a new transaction_code into a table which contains a min_cost and max_cost from the same table. What I am trying to do is take the min_cost from counties with a transaction code of 2 and the max_cost from counties with a transaction_code of 4. Here is what I am so far.
I wrote the errors that I am getting as comments next to the line in which I am getting them. I also tried using the arrays as tables, I didnt think it would work but I left them in there so you would understand what I attemped (they are commented out).
declare
type cost_min_array IS TABLE OF court_cost.cost_range_min%type INDEX BY BINARY_INTEGER;
type cost_max_array IS TABLE OF court_cost.cost_range_max%type INDEX BY BINARY_INTEGER;
v_min_cost cost_min_array;
v_min_cost cost_max_array;
BEGIN
begin
select cost_range_min -- SQL STATEMENT IGNORED
bulk collect into v_min_cost
from court_cost
where state = 'IL'
and transaction_code = 4
and end_date is null
order by county
EXCEPTION WHEN OTHERS THEN --SQL COMMAND NOT PROPERLY ENDED
v_min_cost.delete;
END;
BEGIN
select cost_range_max -- SQL STATEMENT IGNORED
bulk collect into v_max_cost
from court_cost
where state = 'IL'
and transaction_code = 2
and end_date is null
order by county
EXCEPTION WHEN OTHERS THEN -- SQL COMMAND NOT PROPERLY ENDED
v_max_cost.delete;
END;
for indx in 1..v_min_cost.count
loop
insert into court_cost -- SQL STATEMENT IGNORED
(TRANSACTION_CODE,
STATE,
COUNTY,
COST_RANGE_MIN,
BEGIN_DATE,
END_DATE,
DATE_INSERTED,
COURT,
COST_RANGE_MAX)
select lcc.TRANSACTION_CODE,
lcc.STATE,
lcc.COUNTY,
v_min_cost(indx),
lcc.BEGIN_DATE,
lcc.END_DATE,
lcc.DATE_INSERTED,
lcc.COURT,
v_max_cost(indx)
from court_cost lcc
-- cost_min_array cmn,
-- cost_max_array cmx
end loop; -- SQL COMMAND NOT PROPERLY ENDED
end; -- ENCOUNTERED THE SYMBOL ; WHEN EXPECTING LOOP
Any push in the right direction will be greatly appreciated, thanks.

You have missed semicolons thrice in the code which is resulting in the errors . In code down I have added them please check is code runs now. The statement ignored is coming because semi colon is not added at the end of it and even the exception section is considered in the same statement that's why giving error.
declare
type cost_min_array IS TABLE OF court_cost.cost_range_min%type INDEX BY BINARY_INTEGER;
type cost_max_array IS TABLE OF court_cost.cost_range_max%type INDEX BY BINARY_INTEGER;
v_min_cost cost_min_array;
v_min_cost cost_max_array;
BEGIN
begin
select cost_range_min -- SQL STATEMENT IGNORED
bulk collect into v_min_cost
from court_cost
where state = 'IL'
and transaction_code = 4
and end_date is null
order by county--semi colon added
EXCEPTION WHEN OTHERS THEN --SQL COMMAND NOT PROPERLY ENDED
v_min_cost.delete;
END;
BEGIN
select cost_range_max -- SQL STATEMENT IGNORED
bulk collect into v_max_cost
from court_cost
where state = 'IL'
and transaction_code = 2
and end_date is null
order by county--semi colon added
EXCEPTION WHEN OTHERS THEN -- SQL COMMAND NOT PROPERLY ENDED
v_max_cost.delete;
END;
for indx in 1..v_min_cost.count
loop
insert into court_cost -- SQL STATEMENT IGNORED
(TRANSACTION_CODE,
STATE,
COUNTY,
COST_RANGE_MIN,
BEGIN_DATE,
END_DATE,
DATE_INSERTED,
COURT,
COST_RANGE_MAX)
select lcc.TRANSACTION_CODE,
lcc.STATE,
lcc.COUNTY,
v_min_cost(indx),
lcc.BEGIN_DATE,
lcc.END_DATE,
lcc.DATE_INSERTED,
lcc.COURT,
v_max_cost(indx)
from court_cost lcc;--semi colon added
-- cost_min_array cmn,
-- cost_max_array cmx
end loop; -- SQL COMMAND NOT PROPERLY ENDED
end; -- ENCOUNTERED THE SYMBOL ; WHEN EXPECTING LOOP

You're missing semi-colons after your SQL statements. Try
declare
type cost_min_array IS TABLE OF court_cost.cost_range_min%type INDEX BY BINARY_INTEGER;
type cost_max_array IS TABLE OF court_cost.cost_range_max%type INDEX BY BINARY_INTEGER;
v_min_cost cost_min_array;
v_min_cost cost_max_array;
BEGIN
begin
select cost_range_min -- SQL STATEMENT IGNORED
bulk collect into v_min_cost
from court_cost
where state = 'IL'
and transaction_code = 4
and end_date is null
order by county; -- semi-colon added
EXCEPTION WHEN OTHERS THEN --SQL COMMAND NOT PROPERLY ENDED
v_min_cost.delete;
END;
BEGIN
select cost_range_max -- SQL STATEMENT IGNORED
bulk collect into v_max_cost
from court_cost
where state = 'IL'
and transaction_code = 2
and end_date is null
order by county; -- semi-colon added
EXCEPTION WHEN OTHERS THEN -- SQL COMMAND NOT PROPERLY ENDED
v_max_cost.delete;
END;
for indx in 1..v_min_cost.count
loop
insert into court_cost -- SQL STATEMENT IGNORED
(TRANSACTION_CODE,
STATE,
COUNTY,
COST_RANGE_MIN,
BEGIN_DATE,
END_DATE,
DATE_INSERTED,
COURT,
COST_RANGE_MAX)
select lcc.TRANSACTION_CODE,
lcc.STATE,
lcc.COUNTY,
v_min_cost(indx),
lcc.BEGIN_DATE,
lcc.END_DATE,
lcc.DATE_INSERTED,
lcc.COURT,
v_max_cost(indx)
from court_cost lcc; -- semi-colon added
-- cost_min_array cmn,
-- cost_max_array cmx
end loop; -- SQL COMMAND NOT PROPERLY ENDED
end; -- ENCOUNTERED THE SYMBOL ; WHEN EXPECTING LOOP
and see if that improves things.
Share and enjoy.

Related

Using bind variable as table name in Alter table script

FOR k in c2 LOOP
l_sql := 'ALTER TABLE :TABLE_NAME DISABLE CONSTRAINT
:CONSTRAINT_NAME CASCADE';
l_sql :='INSERT INTO TMP_CONSTRAINT (TABLE_NAME,CONSTRAINT_NAME)
VALUES ('':TABLE_NAME'', '':CONSTRAINT_NAME'')';
EXECUTE IMMEDIATE l_sql USING k.TABLE_NAME, k.CONSTRAINT_NAME;
END LOOP;
Above dynamic SQL to take the variable from a cursor and disable constraint accordingly and insert record to a temp table. I am getting error "bind variable does not exist" in the update statement.
It is actually caused by the single code in INSERT
Corrected as per following:
l_sql :='INSERT INTO TMP_ENABLED_CONSTRAINT (TABLE_NAME,CONSTRAINT_NAME) VALUES (:TABLE_NAME ,:CONSTRAINT_NAME)';

dynamically change column in oracle cursor

I have 50 columns in one table and need to update each column.
Trying the below plsql code. (commented update section is working).
But dynamically generated column is not accepting.
(PL/SQL: ORA-01747: invalid user.table.column, table.column, or column specification)
Anybody can help please?
DECLARE
cursor udas is
select 5109 as udaid from dual
union all
select 8209 as udaid from dual;
BEGIN
for uda in udas loop
DECLARE
cursor c1 is
select
x.item, x.uda_id, x.uda_value, x.uda_value_desc
from
hp2_uda_data x
where
x.uda_type='LOV'
and x.uda_id=uda.udaid;
begin
for i in c1 loop
begin
/*update testtable set item_uda_5109_v=i.uda_value,
item_uda_5109_d=i.uda_value_desc where item_code=i.item;*/
update testtable set 'item_uda_'||uda.udaid||'_v'=i.uda_value,
'item_uda_'||uda.udaid||'_d'=i.uda_value_desc where item_code=i.item;
end;
end loop;
commit;
end;
end loop;
END;
Dynamic code requires execute immediate:
execute immediate
'update testtable
set item_uda_'||uda.udaid||'_v = :b1
, item_uda_'||uda.udaid||'_d = :b2
where item_code = :b3'
using i.uda_value, i.uda_value_desc, i.item;
It can be useful to construct the dynamic code in a variable so that you can report or log it in the event of failure.
I also recommend looking into code indentation as a useful technique for making code readable.

Can not perform DML Operation inside a query? While trying to fetch data from collection

here is my PLSQL code:
declare
headerStr varchar2(1000):='C1~C2~C3~C5~C6~C7~C8~C9~C10~C11~C12~C16~C17~C18~C19~RN';
mainValStr varchar2(32000):='1327~C010802~9958756666~05:06AM~NO~DISPOSAL~NDL~4~P32~HELLO~U~28-OCT-2017~28-OCT-2017~Reject~C010741~1;1328~C010802~9958756666~06:07AM~MH~DROP~NDL~1~P32~~U~28-OCT-2017~28-OCT-2017~Reject~C010741~2;1329~C010802~9999600785~01:08AM~BV~DROP~NDL~2~P32~MDFG~U~28-OCT-2017~28-OCT-2017~Reject~C010741~3';
valStr varchar2(4000);
headerCur sys_refcursor;
mainValCur sys_refcursor;
valCur sys_refcursor;
header varchar2(1000);
val varchar2(1000);
iterator number:=1000;
strIdx number;
strLen number;
idx number;
TYPE T_APPROVAL_RECORD IS TABLE OF VARCHAR2(4000) INDEX BY VARCHAR2(1000);
headerTable T_APPROVAL_RECORD;
cnt number;
begin
open headerCur for select * from table(split_str(headerStr,'~'));
open mainValCur for select * from table(split_str(mainValStr,';'));
loop
fetch mainValCur into valStr;
exit when mainValCur%notfound;
insert into header_test values(cnt, valStr); -- for testing purpose
open valCur for select * from table(split_str(valStr,'~'));
loop
fetch valCur into val;
fetch headerCur into header;
exit when valCur%notfound;
exit when headerCur%notfound;
insert into header_test values(header, val);
headerTable(header):= val;
end loop;
idx := headerTable.FIRST; -- Get first element of array
WHILE idx IS NOT NULL LOOP
insert into header_test values (idx, headerTable(idx));
idx := headerTable.NEXT(idx); -- Get next element of array
END LOOP;
headerTable.delete;
end loop;
commit;
end;
c1 c2 ..... c19 are column name and RN is rownumber,
data for the columns of each will be in mainValString seperated by ;
Why i am getting ORA-14551 when i am trying to access collection "headerTable"?
Please help.
Problem is with this line.
idx := headerTable.FIRST;
The index of headertable is of TYPE VARCHAR2 whereas idx is defined as NUMBER.
declare idx as VARCHAR2(1000), it should work.
Having said that, ORA-14551 - Cannot perform DML ... is not related to this error. It is unclear to me why should you encounter this error.
Oh but it does:
EXCEPTION WHEN OTHERS THEN
v_msg:=sqlcode||sqlerrm;
insert into err_data_transfer values('SPLIT_STR',v_msg,sysdate,null);
It may only be during an exception, but it's still DML during a select statement. You may be able to create another procedure as an AUTONOMOUS_TRANSACTION to create the error log. Also, you should either re-raise or raise_application_error afterward. If not your procedure will continue as though the error did not occur; which leads to more problems as to why your main process does not work (including running to completion but doing the wrong thing).

Can anyone help whey my execption section is not working,

My whole intention to catch exception,WRONG parameter is NOT CATCHING exception.
Here is the code:
CREATE OR REPLACE PROCEDURE list_emp (p_emp_id IN employees.employee_id%TYPE,
p_dept_id IN employees.department_id%TYPE)
IS
CURSOR c1 IS
SELECT *
FROM EMPLOYEES
WHERE EMPLOYEE_ID=p_emp_id
AND DEPARTMENT_ID=p_dept_id;
emp_rec c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO emp_rec;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
END LOOP;
CLOSE c1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
END;
When the cursor is opened and fetched with the wrong parameter that does not match any row from the corresponding table, the following line
EXIT WHEN c1%NOTFOUND;
cause the plsql procedure to terminate (because there were no rows found). Hence no exception is raised.
If you do want to display some sort of output you can do the following instead
IF c1%FOUND THEN
dbms_output.put_line('Record Found');
ELSE
dbms_output.put_line('Finished/Done');
EXIT;
END IF;
If you want to raise an error after looping through a cursor that returns no rows, then you're going to have to use a counter to work out how many rows have been processed, and then you can do something if no rows have been processed.
Something like:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
cursor c1 is
select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
v_count number := 0;
begin
for emp_rec in c1
loop
v_count := v_count + 1;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
end loop;
if v_count = 0 then
raise no_data_found;
end if;
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
A few notes:
I converted your cursor loop into a cursor-for-loop; you don't need to worry about declaring the record type and also Oracle handles the opening and closing of the cursor for you.
I added raise; to each of your exception handlers - in general, having when others then null (which is effectively what your original code was doing - no errors are raised to the calling code) is a bad idea. I added the raise to the no_data_found condition as that wasn't doing anything either; typically, if you have an exception condition, you want it to do something to let the calling code know there was a problem (not always, of course; sometimes you don't want the processing to stop if a particular error condition is met).
Your cursor was selecting all columns, but in your procedure, you were only using three of them. I've therefore amended the cursor so that it only pulls back those three columns.
Don't rely on dbms_output in your production code. Code that calls this procedure won't see anything populated in dbms_output, unless it explicitly looks for it - and that's not something I've ever seen in any production code, outside of Database tools (eg. SQL*Plus, Toad, etc). I've left this in your procedure as I've a feeling this is a learning exercise for you, but please don't think that this is in any way acceptable in production code.
You're passing p_emp_id in as a parameter - typically, that's the primary key of the employees table. If that's the case, then there's no need for the cursor for loop at all - you could do it by using select ... into ... instead, like so:
.
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
v_emp_id employees.employee_id%type;
v_first_name employees.first_name%type;
v_last_name employees.last_name%type;
begin
select employee_id,
first_name,
last_name
into v_emp_id,
v_first_name,
v_last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
Alternatively, just pass back a ref cursor:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type,
p_ref_cur out sys_refcursor)
is
begin
open p_ref_cur for select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
-- No need for an exception handler here since you're not storing the error details anyway.
-- By not having an error handler, any error will automatically be raised up to the calling code
-- and it will have the correct error stack trace info (e.g. the line number the error occurred,
-- rather than the line the error was reraised from
end;
/
And to run the ref cursor in SQL*Plus (or as a script in Toad/SQL Developer/etc), you do the following:
-- create a variable outside of PL/SQL to hold the ref cursor pointer (this is a SQL*Plus command):
variable rc refcursor;
-- populate our ref cursor variable with the pointer. Note how we pass it in as a bind variable
begin
list_emp(p_emp_id => 1234,
p_dept_id => 10,
p_ref_cur => :rc);
end;
/
-- finally, print the contents of the ref cursor.
print rc;

ROWTYPE definition in pl/sql

I have written a PL/SQL Procedure which compares data between two tables and print the difference if any, but the twist is the table names to the procedure is dynamic. Here is the procedure
create or replace PROCEDURE compareTables(
tabA IN VARCHAR2, tabB IN VARCHAR2) AS
cur_tab_name USER_TABLES%ROWTYPE;
lv_sql varchar2(4000);
lv_sql2 varchar2(4000);
BEGIN
--SELECT TABLE_NAME INTO cur_tab_name FROM USER_TABLES WHERE TABLE_NAME = tabA;
lv_sql2 := 'SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME = :b_tabA';
EXECUTE IMMEDIATE lv_sql2 INTO cur_tab_name USING tabA;
<<child>>
DECLARE
TYPE cursor_ref IS REF CURSOR;
cur_comp_result cursor_ref;
rec_comp_result cur_tab_name.TABLE_NAME%rowtype;
BEGIN
lv_sql := 'SELECT * FROM '||tabA||' MINUS SELECT * FROM '||tabB;
OPEN cur_comp_result FOR lv_sql;
LOOP
FETCH cur_comp_result INTO rec_comp_result;
EXIT WHEN cur_comp_result%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(rec_comp_result.empid || '' || rec_comp_result.name);
END LOOP;
CLOSE cur_comp_result;
Exception
When others then
dbms_output.put_line('The Problem is '||sqlerrm);
END;
END compareTables;
Now the problem is when I compile this procedure I am getting the following error
Error at line 14: PLS-00310: with %ROWTYPE attribute, 'CUR_TAB_NAME.TABLE_NAME' must name a table, cursor or cursor-variable
line 14:rec_comp_result cur_tab_name.TABLE_NAME%rowtype;
how will I solve it?
*NB: I don't have oracle installed in my system. I am using Oracle Apex Online tool which uses
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 and
PL/SQL Release 11.2.0.3.0
As a test, go to the last line, and after the semicolon, hit enter.
I know that Pro*C in particular will gag without a line terminator at the end of the file.
You may be encounting that issue.
Outside the scope of your question consider
SELECT columns
FROM TABLE1
MINUS
SELECT columns
FROM TABLE2
and
SELECT columns
FROM TABLE2
MINUS
SELECT columns
FROM TABLE1
Use: cur_tab_name.table_name. The variable CUR_TAB_NAMEis of type USER_TABLE%ROWTYPE, thus it has several fields.

Resources