I have a report with the following structure:
Column 1 ID, Column 2 Approved_Rejected_Status, Column 3 Rep_Id
I'm getting a no data found error while running the following:
DECLARE
V_REP_ID VARCHAR2(100);
V_ROWS_APPROVED_min NUMBER;
V_ROWS_APPROVED_max NUMBER;
V_STAT VARCHAR2(100);
BEGIN
SELECT min(id) INTO V_ROWS_APPROVED_min FROM MY_TABLE;
SELECT max(id) INTO V_ROWS_APPROVED_max FROM MY_TABLE;
FOR i IN V_ROWS_APPROVED_min..V_ROWS_APPROVED_max LOOP
SELECT APPROVED_REJECTED_STATUS INTO V_STAT
FROM MY_TABLE WHERE MY_TABLE.ID =i;
SELECT REP_ID INTO V_REP_ID
FROM MY_TABLE
WHERE MY_TABLE.ID = i;
END LOOP;
END;
I think it has something to do with the ID having non consecutive values perhaps?
(I can't have consecutive values on the report for functionality, neither include an exception cause I need to to perform actions depending on the type_of_change)
Thank you
Edit:
Thank you MT0 for your review/suggestion!
I tried using a cursor for loop and something really weird is happening:
for cur_m in (select id, approved_rejected_status
from bdc_bench_watchlist_t
order by id)
loop
if cur_m.approved_rejected_status = 'Approved' then
--Inserting into a test table:
insert into index_test (ID_COPY, MY_APPROVED_STATUS)
values (cur_m.id, cur_m.approved_rejected_status);
end if;
end loop;
The result brings all the range of IDs (from the min to the max) ok but! the approved_rejected_status column is empty (only the header is showing with null data on the rows). I have no idea why.
If I do a basic select id, approved_rejected_status from the source table it brings everything ok (id and status info ok).
Thank you
Assuming you need to iterate through every number and do something even if there is no row there then just handle the NO_DATA_FOUND exception:
DECLARE
V_REP_ID MY_TABLE.REP_ID%TYPE;
V_ROWS_APPROVED_min MY_TABLE.ID%TYPE;
V_ROWS_APPROVED_max MY_TABLE.ID%TYPE;
V_STAT MY_TABLE.APPROVED_REJECTED_STATUS%TYPE;
BEGIN
SELECT min(id), max(id)
INTO V_ROWS_APPROVED_min, V_ROWS_APPROVED_max
FROM MY_TABLE;
FOR i IN V_ROWS_APPROVED_min..V_ROWS_APPROVED_max LOOP
BEGIN
SELECT APPROVED_REJECTED_STATUS, REP_ID
INTO V_STAT, V_REP_ID
FROM MY_TABLE
WHERE MY_TABLE.ID =i;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_STAT := NULL; -- Or put default values here.
V_REP_ID := NULL;
END;
-- Do stuff with the stat/rep_id
END LOOP;
END;
If you don't want to iterate through every row and want to skip the missing id values then you could use a cursor and order by id.
Related
I am trying to write a code block where record insert if record already exist then update
table. i am trying If (sql%rowcount = 0) then but it's not working in cursor and more one records.
What I tried so far as the code block is
declare
remp_id varchar2(60);
remp_name varchar2(100);
rdesig varchar2(100);
rdept_no number;
rdesig_no number;
rdept_name varchar2(60);
cursor alfa is
select emp_code, emp_name, desig, dept_name, dept_no, desig_no
from emp
where emp_code between :first_code and :second_code;
begin
open alfa;
loop
fetch alfa
into remp_id, remp_name, rdesig, rdept_name, rdept_no, rdesig_no;
exit when alfa%notfound;
update att_reg_mo
set emp_code = remp_id,
emp_name = remp_name,
desig = rdesig,
dept_name = rdept_name,
dept_no = rdept_no,
desig_no = rdesig_no,
att_date = :att_date,
emp_att = :emp_att,
att_type = 'MA',
reg_date = :reg_date
where emp_code between :first_code and :second_code
and reg_date = :reg_date
and att_date = :att_date;
commit;
if (sql%rowcount = 0) then
insert into att_reg_mo
(emp_code,
emp_name,
desig,
dept_name,
att_date,
emp_att,
att_type,
reg_date,
dept_no,
desig_no)
values
(remp_id,
remp_name,
rdesig,
rdept_name,
:att_date,
:emp_att,
'MA',
:reg_date,
rdept_no,
rdesig_no);
end if;
commit;
end loop;
close alfa;
end;
when i am fire the trigger then record is insert but where need to update record it's update with null values
Or you could use something like that:
DECLARE
cursor test is
select 1 as v from dual
union
select 2 as v from dual;
n_var NUMBER;
BEGIN
for rec in test loop
BEGIN
select 1 into n_var from dual where rec.v=2;
DBMS_OUTPUT.PUT_LINE('Please Update Any Table');
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE('Please Insert Any Table');
END;
end loop;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Unexpected error');
END;
SQL%attribute always refers to the most recently run SELECT or DML statement. It refreshes as to start from zero after any of transaction statement such as COMMIT, ROLLBACK or SAVEPOINT is issued, for a session in which AUTOCOMMIT is presumed to be OFF by default. Therefore, you always get zero from SQL%ROWCOUNT which is just before the INSERT statement, and keeping inserting to the concerned table during every run of code block.
So, remove the f i r s t COMMIT, removing also keeps the atomicity of the whole transaction, from your code block.
Demo
I have a timestamp column in one of my tables. I want to get all entries that are a minute after the very first entry in the table. The timestamp is chronological with respect to the primary id.
To this end I retrieve the first entry in the table and put the value of the time column into a variable. Then I add a minute to this value and create a new variable with the new value. Using this new variable I search for the entry that is less than the adjusted timestamp and retrieve the id of that entry.
So my three variables are v_time, v_adjusted and v_id.
When setting v_time I have the following query,
begin
select time_of
into v_time
from MYTABLE where rownum=1
order by id asc;
exception
when no_data_found then
v_time := null;
end;
For v_adjusted i simply do,
v_adjusted := v_time + 1 / 1440.00;
When setting v_id I have,
begin
select id
into v_id
from MYTABLE where time_of < v_adjusted and rownum=1
order by id desc;
exception
when no_data_found then
v_id:= null;
end;
These operations do not return the correct result. The retrieved id is wrong. I can confirm by executing a query with the new timestamp which returns the correct id. As an example, in my table adding a minute should return id 19 but the value of v_id is 1.
Why am I getting the wrong result?
You need to use subquery:
begin
SELECT id
INTO v_id
FROM (select id
from MYTABLE where time_of < v_adjusted
order by id desc
) sub
where rownum=1;
exception
when no_data_found then
v_id:= null;
end;
or simply max:
begin
select MAX(id)
into v_id
from MYTABLE where time_of < v_adjusted;
exception
when no_data_found then
v_id:= null;
end;
EDIT:
With FETCH (Oracle 12c):
begin
select id
into v_id
from MYTABLE where time_of < v_adjusted;
ORDER BY id DESC
FETCH FIRST 1 ROWS ONLY;
exception
when no_data_found then
v_id:= null;
end;
I have a temp table(around 760k rows ) with no primary key. i am trying to insert rows from this temp table to my main table using bulk collect and forall(also used save exceptions to catch the rejected rows) , i have successfully done this. But i need to keep track of rows which are getting rejected and which are moved succesfully(what i want to do is update status column of temp table as 'E' for error and M for succeeful migration)
here is my procedure:
code snippet
desc temp_table:
col1 varchar2(30);
col2 varchar2(30);
col3 number;
col4 number;
status varchar2(1);
create or replace procedure mov_to_main_table
as
loop_count number default 0;
error_row_no number default 0;
sql_stmt varchar2(500);
cursor c_data is
select * from temp_table,a
where temp_table.col1=a.col;
TYPE t_bulk_collect_tab IS TABLE OF c_data%ROWTYPE;
l_tab t_bulk_collect_tab;
l_inserted t_bulk_collect_tab;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 1000;
EXIT WHEN l_tab.count = 0;
BEGIN
FORALL i IN 1..l_tab.count save exceptions
insert into main_table(col1,col2,col3)
values(l_tab(i).col1,l_tab(i).col2,l_tab(i),col3);
EXCEPTION
when others then
bulk_error_count := sql%bulk_exceptions.count;
--dbms_output.put_line('number of error rows :'||bulk_error_count );
for i in 1..bulk_error_count
loop
error_row_no := to_number(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX)+100*loop_count;
sql_stmt := 'update temp_table set status=''E'' where rowid in (select rowid from temp_table where rownum <=:1 minus select rowid from temp_table where rownum<:2)';
execute immediate sql_stmt using error_row_no,error_row_no;
end loop;
end;
In above code i am updating the row which went into a exception part as 'E'
but for the row which is inserting succesfully iam unable to update the status ,
primarily i need to catch that rownum or rowid to update which iam unable to get from for all
how to update the succefully moved row?
please help me out
thanks in advance..
You approach is quite clever but there are a few things that won't work properly:
First, you don't need dynamic SQL here, drop that execute immediate.
You can still access the l_tab nested table in the exception block, so it's easy to pick the relevant id or rowid.
Last, this is not how paging queries are used in Oracle, see examples on SO.
My advice would be to fetch the rowid at the same time as the rest of the data:
CURSOR c_data IS
SELECT temp_table.*, a.*, temp_table.rowid rid
FROM temp_table, a
WHERE temp_table.col1 = a.col;
Then in the exception block you could find and update the offending row:
EXCEPTION
WHEN OTHERS THEN
bulk_error_count := SQL%bulk_exceptions.count;
--dbms_output.put_line('number of error rows :'||bulk_error_count );
FOR i IN 1 .. bulk_error_count LOOP
UPDATE temp_table
SET status = 'E'
WHERE rowid = l_tab(SQL%BULK_EXCEPTIONS(i).error_index).rid);
END LOOP;
END;
I have one table called event, and created another global temp table tmp_event with the same columns and definition with event. Is it possible to insert records in event to tmp_event using this ?
DECLARE
v_record event%rowtype;
BEGIN
Insert into tmp_event values v_record;
END;
There are too many columns in event table, I want to try this because I don't want to list all the columns.
Forget to mention: I will use this in the trigger, can this v_record be the object :new after insert on EVENT table ?
To insert one row-
DECLARE
v_record event%rowtype;
BEGIN
SELECT * INTO v_record from event where rownum=1; --or whatever where clause
Insert into tmp_event values v_record;
END;
Or a more elaborate version to insert all rows from event-
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF event%ROWTYPE;
l_tab t_bulk_collect_test_tab;
CURSOR c_data IS
SELECT *
FROM event;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 10000;
EXIT WHEN l_tab.count = 0;
-- Process contents of collection here.
Insert into tmp_event values v_record;
END LOOP;
CLOSE c_data;
END;
/
In a trigger, yes it is possible but its like the chicken or the egg. You have to initialize every field of the rowtype with the :new column values like-
v_record.col1 := :new.col1;
v_record.col2 := :new.col2;
v_record.col3 := :new.col3;
....
Apparently, the PLSQL examples above cannot be used in a trigger since it would throw a mutating trigger error. And there is no other way for you to get the entire row in the trigger other than accessing each column separately as I explain above, so if you do all this why not directly use :new.col in the INSERT into temp_event itself, will save you a lot of work.
Also since you say it's a lot of work to mention all the columns, (in Oracle 11gR2) here's a quick way of doing that by generating the INSERT statement and executing it dynamically (although not tested for performance).
CREATE OR REPLACE TRIGGER event_air --air stands for "after insert of row"
AFTER INSERT ON EVENT
FOR EACH ROW
L_query varchar2(2000); --size it appropriately
BEGIN
SELECT 'INSERT INTO tmp_event VALUES ('|| listagg (':new.'||column_name, ',')
WITHIN GROUP (ORDER BY column_name) ||')'
INTO l_query
FROM all_tab_columns
WHERE table_name='EVENT';
EXECUTE IMMEDIATE l_query;
EXCEPTION
WHEN OTHERS THEN
--Meaningful exception handling here
END;
There is a way to insert multiple rows into table with %Rowtype.
checkout below example.
DECLARE
TYPE v_test IS TABLE OF TEST_TAB%rowtype;
v_test_tab v_test ;
EXECUTE immediate ' SELECT * FROM TEST_TAB ' bulk collect INTO v_test_tab ;
dbms_output.put_line('v_test_tab.count -->'||v_test_tab.count);
FOR i IN 1..v_test_tab.count
LOOP
INSERT INTO TEST_TAB_1 VALUES v_test_tab
(i
) ;
END LOOP;
END;
sum up to full working excample ...
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF event%ROWTYPE;
l_tab t_bulk_collect_test_tab;
CURSOR c_data IS SELECT * FROM event;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 10000;
EXIT WHEN l_tab.count = 0;
FORALL i IN 1..l_tab.count
Insert into tmp_event values l_tab(i);
commit;
END LOOP;
CLOSE c_data;
END;
/
I have a table with 2 varchar2 columns. I have added new number column to existing table to make this column primary key. This table now includes 3 columns. I gave a try to use anonymous block as following:
declare
cnt number;
begin
select nvl(count(*),0) into cnt from sometable;
for i in 1..cnt
loop
update sometable set id=i where i=rownum;
end loop;
end;
Using this anonymous block updates this table unexpectedly.
My solution was to use the following statement:
create table sometablecopy as select row_number() over(order by sometable.col1) as id, sometable.* from sometable;
Nevertheless I am curios why doesn't anonymous block produce expected primary key values with the help of rownum pseudocolumn? It must be rownum related issue.
Rownum is a pseudocolumn. Its assigned to rows as they are returned from the select. So you can't say "select * from my_table where rownum = 42" since the row with rownum=42 hasn't been defined yet, it will vary depending on your select and predicate (and "select * from my_table where rownum = 1" will return a single row, not the "first" row, whatever that would be). You could do something like (untested):
declare
cursor sel_cur is
select rowid as row_id
from my_table
order by orderby_col;
v_ctr pls_integer := 0;
begin
for rec in sel_cur
loop
v_ctr := v_ctr + 1;
update my_table set pk_col = v_ctr where rowid = rec.row_id;
end loop;
commit;
exception
when others then
rollback;
raise;
end;
This assumes you have sufficient rollback to update the entire table.
Hope that helps.
You cannot use ROWNUM like that (see ROWNUM in SQL).
What you could have done is this:
UPDATE sometable SET id = ROWNUM;