well
A reader, September 06, 2001 - 4:10 pm UTC
How about telling us how to do the same in pl/sql
September 07, 2001 - 11:48 am UTC
Sure, it looks very much the same:
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
2 type array is table of varchar2(20);
3
4 l_deptno array := array( 100,10,20,30 );
5 l_return_deptno array;
6 l_dname array := array( 'dept1', 'dept2', 'dept3', 'dept4' );
7 begin
8 forall i in 1 .. l_deptno.count
9 UPDATE DEPT
10 SET DNAME = l_dname(i)
11 WHERE DEPTNO = l_deptno(i)
12 RETURNING DEPTNO BULK COLLECT INTO l_return_deptno;
13
14 for i in 1 .. l_return_deptno.count
15 loop
16 dbms_output.put_line( 'updated deptno = ' || l_return_deptno(i) );
17 end loop;
18 end;
19 /
updated deptno = 10
updated deptno = 20
updated deptno = 30
PL/SQL procedure successfully completed.
Thanks Tom
Robin, October 04, 2001 - 4:56 pm UTC
Most likely the next thing we want to do is an INSERT
of the rows that weren't updated.
As far as I know, the only way to do this is by checking
each key returned by the RETURNING clause against the keys
we sent for UPDATE. The ones that DIDN'T come back are the
ones we need to INSERT. Copy those into another array,
then do a bulk INSERT.
This requires going through the arrays in a loop,
whether you use PRO*C or PL/SQL, comparing one key per
iteration. I believe that the keys are returned
in the same order they were sent, or this wouldn't work.
It would be nice if Oracle could return the row numbers
that were updated (1,2,3 in your DEPT example), then the
program could more easily copy the other rows to the
array used for insert.
Or have I missed something?
October 04, 2001 - 7:58 pm UTC
Always something more ;)
Ok, here is a quickie example in PLSQL that shows one alternative method:
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table dept;
Table dropped.
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table dept as select * from scott.dept;
Table created.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
2 type array is table of varchar2(20);
3
4 l_deptno array := array( 99,10,20,30 );
5 l_return_deptno array;
6 l_dname array := array( 'dept1', 'dept2', 'dept3', 'dept4' );
7 begin
8 forall i in 1 .. l_deptno.count
9 UPDATE DEPT
10 SET DNAME = l_dname(i)
11 WHERE DEPTNO = l_deptno(i)
12 RETURNING DEPTNO BULK COLLECT INTO l_return_deptno;
13
14 forall i in 1 .. l_deptno.count
15 insert into dept ( deptno, dname )
16 select l_deptno(i), l_dname(i)
17 from dual
18 where NOT EXISTS (select null from dept where deptno = l_deptno(i));
19
20 for i in 1 .. l_return_deptno.count
21 loop
22 dbms_output.put_line( 'updated deptno = ' || l_return_deptno(i) );
23 end loop;
24 end;
25 /
updated deptno = 10
updated deptno = 20
updated deptno = 30
PL/SQL procedure successfully completed.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 dept2 NEW YORK
20 dept3 DALLAS
30 dept4 CHICAGO
40 OPERATIONS BOSTON
99 dept1
and if you have 9i, you can do it in one statment:
ops$tkyte@ORA9I.WORLD> drop table dept;
Table dropped.
ops$tkyte@ORA9I.WORLD> create table dept as select * from scott.dept;
Table created.
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> declare
2 type array is table of varchar2(20);
3
4 l_deptno array := array( 99,10,20,30 );
5 l_return_deptno array;
6 l_dname array := array( 'dept1', 'dept2', 'dept3', 'dept4' );
7 begin
8 forall i in 1 .. l_deptno.count
9 merge into dept
10 using ( select l_deptno(i) deptno, l_dname(i) dname from dual ) d
11 on ( dept.deptno = d.deptno )
12 when matched then
13 update set dept.dname = d.dname
14 when NOT matched then
15 insert ( deptno, dname ) values ( d.deptno, d.dname );
16 end;
17 /
PL/SQL procedure successfully completed.
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 dept2 NEW YORK
20 dept3 DALLAS
30 dept4 CHICAGO
40 OPERATIONS BOSTON
99 dept1
Error in Bulk "insert"
READER, October 05, 2001 - 9:37 am UTC
HI, Tom,
In the above example, if there is any exception happened in the course of bulk "insert", let say ORA-1400 and/or others errors happened for one may be two rows(but for the other rows, they are succeed), what is the best way and how we handle these potential error? That is we let them continue insert or rollback the whole process for all rows? And how?
Please address both in proC and pl/sql environment and 9i by using "merge".
Thanks
October 05, 2001 - 11:54 am UTC
You tell ME what is acceptable. Me, I would reject the batch (easiest to code, never understood the benefit of a partial batch). In that case -- just ROLLBACK when you get the error and all is UNDONE. You could go a step further and use savepoints:
begin
savepoint foo;
insert
update
merge
whatever.....
exception
when others then rollback to savepoint; RAISE;
end;
insert with array of structures stops at the first error
somala subba, May 22, 2002 - 8:19 pm UTC
100 records in the array to be inserted. Some error in record 5 but the rest are fine.
When i do insert into temp_table values(:array)
it stops on record 5. returns sqlerrd[2]= 4.
Is there anyway to get all 99 records inserted except for the error rows in the above statement.
May 22, 2002 - 11:04 pm UTC
You have to retry the insert, passing array+5 as the host variable (to skip over the rows loaded and the bad row).
Even SQLLDR does this.
why this happening -- 8.1.6
John, April 02, 2003 - 6:39 pm UTC
Tom,
in the below, pl/sql block, i see the the variable v_total_amount(which is declared in other part of the code) becoming null when there is no rows found by the update statement.
Begin
--
update sal = sal + least(sal *.25,v_total_amount)
where empno = :i_empno
and active = 'Y'
returning v_total_amount - nvl(least(sal*.25,v_total_amount),0) into v_total_amount;
--
end;
am i doing wrong anywhere?
April 02, 2003 - 9:20 pm UTC
the update suceeded.
hence, it returned the value -- NULL (unknown).
No but, desired, expected behaviour.
If not what you want, code:
update ... returning into some_temp_variable;
if (sql%rowcount > 0 ) then
v_total_amount := some_temp_variable;
end if;
"no rows found" is not an error, no problem, not an issue with an update. It is perfectly normal in fact.
savepoint information
Anurag, April 28, 2003 - 8:57 am UTC
Tom , I was issuing savepoint while doing certain transactions, I wanted to know that Can I retrieve the savepoint information , if Yes which view should I query. Also, If I commit, does this information is lost or marked as committed.
Regards,
April 28, 2003 - 9:25 am UTC
savepoints are internal to your logic (hopefully, you would never need query them as transaction control is so vital, so important -- that you would know exactly where they are)
When you commit -- this information is "not relevant". It is not that it is lost, it quite simply isn't relevant.
A savepoint is just the ability for you to rollback a subset of your transaction -- it affects commits not at all, not even a little bit.
savepoint
Anurag, April 29, 2003 - 5:54 am UTC
Thank you Tom, for clearing my doubts. I appreciate, your quick reply. Thanks once again.
returning thing I inserted
A reader, November 24, 2003 - 2:53 am UTC
Hi
I would like to know what rows I inserted in a table, I tried to use
declare
type array is table of dept.deptno%type;
l_deptno array;
begin
insert into dept
select * from x where not exists (select null from dept where x.deptno = dept.deptno)
RETURNING DEPTNO BULK COLLECT INTO l_deptno;
for i in 1 .. l_deptno.count
loop
dbms_output.put_line(l_deptno(i));
end loop;
end;
/
but gives errors... I dont know why :-0
November 24, 2003 - 8:15 am UTC
it does not appear to be a supported construct -- it only works with "values", not select (which seems backwards since values can only insert a row -- bulk collect doesn't make sense)
bulk collect into records
A reader, November 24, 2003 - 3:18 am UTC
Hi
Can do we return records into collections in 8.1.7
such as
insert into a select * from b
returning record bulk collect into array
November 24, 2003 - 8:15 am UTC
nope.
returning things i inserted
A reader, November 24, 2003 - 11:30 am UTC
Hi
It sounds odd that bulk collect does not work with insert select. Insert select is bulk!
Anyway, is there anyway to know what rows I inserted?
Cheers
November 24, 2003 - 12:47 pm UTC
short of
a) bulk collect them
b) forall i insert them
instead of insert into as select -- not really.
Merge and update/insert count
karma, January 15, 2004 - 2:01 pm UTC
In using Merge statment how would i how many rows got updated and how many rows got inserted?
January 15, 2004 - 2:12 pm UTC
you cannot, they are "merged" -- period.
some are inserted, some are updated -- all are "merged"
Which Column
A reader, March 10, 2004 - 2:50 am UTC
Hi Tom,
If we update two columns in a update statement
UPDATE <table_name> SET <column_1>=<value_1>,
<column_2>=<value_2>
;
And the following message is received
ORA-01722: invalid number
Is there a way to know if the invalid number was for <column_1> or <column_2>
Also, is there a way to let <column_1> gets updated if there is not a datatype mismatch and let <column_2> fail if datatype for this is not correct? ie. enabling partial updates?
I know this is very basic, but your comments as always are highly appreciated and welcome.
Thanks
March 10, 2004 - 8:58 am UTC
No, there is not (for both questions)
statements are always atomic - they either happen 100% sucessfully, or they do not happen at all.
Failure for individual record during bulk update
Shimon, March 24, 2004 - 9:24 am UTC
Hi Tom!
I liked your answer to the question prsented in the title very much . The problem is that using "...returning DEPTNO BULK COLLECT into ..." returns only the successful rows, that were actually updated. I'd like to stop the whole process even if only one fails because no matching record found, and print the exact data that caused the failure (i.e., print something like : " 'The process failed due to NO_DATA_FOUND for Department number '||l_deptno(i) ").
I hope it is possible....
Thanks in advance,
Shimon B.
March 24, 2004 - 9:42 am UTC
"no matching record found" is NOT AN ERROR in any way shape or form!
(i never understand this point of confusion really -- select * from t where 1=0 is not an error, update t set x = 55 where 1=0 is NOT and error and so on)
If you update by primary key -- and you bulk collect back the primary keys (and you don't update the primary keys), you'll know what rows got updated and which did not.
weakness
A reader, March 14, 2005 - 6:54 am UTC
Hi
From your example above
int return_deptno[4];
exec sql end declare section;
EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();
EXEC SQL WHENEVER SQLWARNING DO sqlerror_hard();
EXEC SQL CONNECT :oracleid;
EXEC SQL
UPDATE DEPT
SET DNAME = :dname
WHERE DEPTNO = :deptno
RETURNING DEPTNO into :return_deptno;
You set your array to 4 becase you think you are going to update 4 rows, what if the update affects less than or ore than 4 rows? Specifically more than 4, how can we handle that? Ned a loop for that I guess?
March 14, 2005 - 8:10 am UTC
deptno would be the primary key here.
if I send in 4 deptnos -- how many rows will I update? 4.......
how do you handle array if you dont know how mnay you will delete
A reader, March 15, 2005 - 10:14 am UTC
Hi
Say we have this PRO*C code
#define TAM_ARRAY 100
int return_empno[TAM_ARRAY];
exec sql end declare section;
EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();
EXEC SQL WHENEVER SQLWARNING DO sqlerror_hard();
EXEC SQL CONNECT :oracleid;
EXEC SQL
DELETE EMP
WHERE DEPTNO = :deptno
RETURNING EMPNO into :return_empno;
My array can fit 100 elements, what happens if
1. DELETE gets rid of over 100 rows
2. DELETE only gets rid of 67 rows for example
What will the array be filled with in the first case and the second?
If we want further process data with the data we deleted how can we reference them? How do we know we only have to process X rows and not 100
An example will be much appreciated!
Thank you very much
March 15, 2005 - 10:21 am UTC
1) ora error
2) sqlca.sqlerrd[2] tells you the number of rows affected by DML
static void die(void)
{
char errmsg[513];
int length;
int real_length = sizeof(errmsg);
exec sql whenever sqlerror continue;
sqlglm( errmsg, &real_length, &length );
errmsg[length] = 0;
fprintf( stderr, "\n%s\n", errmsg );
fprintf( stderr, "sqlcode = %d\n", sqlca.sqlcode );
exit(1);
}
void process(void)
{
exec sql begin declare section;
int i;
int empnos[10];
exec sql end declare section;
exec sql whenever sqlerror continue;
exec sql drop table emp;
exec sql whenever sqlerror do die();
exec sql create table emp as select * from scott.emp;
exec sql delete from emp where rownum <= 5
returning empno into :empnos;
printf( "%d rows affected\n", sqlca.sqlerrd[2] );
exec sql rollback;
exec sql delete from emp returning empno into :empnos;
}
will print out:
[tkyte@xtkyte-pc t]$ ./t
connecting
connected
5 rows affected
ORA-24343: user defined callback error
sqlcode = -24343
[tkyte@xtkyte-pc t]$
so, 5 rows affected the first time and ora-24343 the second.
how to insert the "REAL" number of elements (element with meaninful data)
A reader, March 15, 2005 - 10:45 am UTC
Hi
From your code
void process(void)
{
exec sql begin declare section;
int i;
int empnos[10];
exec sql end declare section;
exec sql whenever sqlerror continue;
exec sql whenever sqlerror do die();
exec sql delete from emp where rownum <= 5
returning empno into :empnos;
printf( "%d rows affected\n", sqlca.sqlerrd[2] );
exec sql rollback;
exec sql delete from emp returning empno into :empnos;
exec sql insert into table_a(empno) values :empnos; /*ADDED */
}
Imagine this, the delete gets rid of 5 rows but the insert will insert 10 rows, 5 rows with zeros, how can we avoid that?
If I do this
exec sql delete from emp returning empno into :empnos;
iCount = sqlca.sqlerrd[2]
exec sql for :iCount
insert into table_a(empno) values :empnos;
then it works quite happily however I am not sure if there are any drawbacks
regards
March 15, 2005 - 11:47 am UTC
It would not necessarily insert 10 rows with 5 zeros.
it would insert 10 rows (assuming the data passed constraints) with 5 of the rows having "garbage" in them since you never initialized the array.
So, you use the "for :array_size" to control the number of rows to insert. It is the only correct way to do it.
update returning bulk collect into - unexpected behaviour
k., September 15, 2005 - 9:58 am UTC
hi tom,
i have a problem with "update..returning..bulk collect into". i am not sure whether it's a bug or this
behaviour is acceptable but sometimes (once in 10000 cases or even less frequently) when i execute
an update which updates N rows, more than N rows
are returned by the returning clause (some rows
appear twice).
oracle version is: 9.2.0.6.0 running on windows xp
thanks for your reply
September 15, 2005 - 10:17 am UTC
how do you know it updated only N rows? what does the code look like
..
k., September 15, 2005 - 10:42 am UTC
i know it updated N rows beacause:
1) the update contains "rownum<=N"
2) when i do:
begin
update a_table
set col = 'new_value'
where col = 'old_value'
and rownum <= 10
returning rowid
bulk collect into tab_rowids;
for i in tab_rowids.first..tab_rowids.last
loop
dbms_output.put_line(tab_rowids(i));
end loop;
end;
i see 10 updated rows in "a_table"
and more than 10 lines output in the loop
(some rowids appear twice - so the bulk collect returns
the SAME record twice)
although it is very rare to happen, i need to add duplicity
checks, because i cannot afford it to happen at all.
i can send you a script showing this behaviour,
i just thought that maybe this construct is "known"
to behave like this under certain circumstances.
September 15, 2005 - 11:28 am UTC
reproduces in 9i, but not 10g....
Write consistency, they messed up, I would definitely consider this a product issue. Do you have support? I have a really small test case for you to use:
drop table t;
set echo on
create table t ( x int, y int );
insert into t values ( 1, 1 );
insert into t values ( 2, 2 );
create or replace trigger t_trigger
before update on t for each row
begin
dbms_output.put_line( 'old: ' || :old.x || ', ' || :old.y );
dbms_output.put_line( 'new: ' || :new.x || ', ' || :new.y );
end;
/
update t set y = 42 where x = 2;
set echo off
prompt in another window:
prompt
prompt declare
prompt type array is table of rowid;;
prompt l_data array;;
prompt begin
prompt update t
prompt set y = 55
prompt returning rowid bulk collect into l_data;;
prompt dbms_output.put_line( sql%rowcount );;
prompt dbms_output.put_line( l_data.count );;
prompt for i in 1 .. l_data.count loop
prompt dbms_output.put_line( l_data(i) );;
prompt end loop;;
prompt end;;
prompt /
set echo on
pause
commit;
the other session will show:
ops$tkyte@ORA9IR2> edit
Wrote file afiedt.buf
1 declare
2 type array is table of rowid;
3 l_data array;
4 begin
5 update t
6 set y = 55
7 returning rowid bulk collect into l_data;
8 dbms_output.put_line( sql%rowcount );
9 dbms_output.put_line( l_data.count );
10 for i in 1 .. l_data.count loop
11 dbms_output.put_line( l_data(i) );
12 end loop;
13* end;
ops$tkyte@ORA9IR2> /
old: 1, 1
new: 1, 55
old: 2, 2
new: 2, 55
old: 1, 1
new: 1, 55
old: 2, 42
new: 2, 55
2
3
AAAK5CAAGAAAABIAAA
AAAK5CAAGAAAABIAAA
AAAK5CAAGAAAABIAAB
If write consistency doesn't mean anything to you -- it is OK, I made it up. This describes it however:
http://asktom.oracle.com/Misc/something-different-part-i-of-iii.html http://asktom.oracle.com/Misc/part-ii-seeing-restart.html http://asktom.oracle.com/Misc/part-iii-why-is-restart-important-to.html
I didn't anticipate that happening - in fact I almost hit submit on an answer with "beats me..." and just before I hit it, I thought "i wonder" and sure enough - it was the cause.
write consistency
k., September 16, 2005 - 4:19 am UTC
thanks!
it fits in the scenario - we have multiple processes
executing the update and since the modified column
is also the column that appears in the where clause
restarts do happen from time to time.
we do have support, so i'll tell our dba to contact
them.
anyway, i have modified my code to check for duplicity in the array (i first took a chance and tried
"returning DISTINCT col1,col2,.. bulk collect into ...." :-) )
have a nice day
:-)
FORALL insert
Anne, October 12, 2005 - 3:31 pm UTC
Hi Tom,
Thanks to your great site!! I am doing a bulk collect fetch from a cursor, and want to do a FORALL insert into a table for just the distinct customer_ids in the cursor - Please advise on how this can be done.
declare
cursor hrs_cur is
SELECT
tm.agency_nbr agency_nbr
, pc.customer_id customer_id
, pc.percent cust_percent
, p.project_id project_id
, ....
, sum(tm.hrs_qty) OVER (PARTITION ...) cust_proj_rate_hrs
, tm.audit_stamp_dt
from .......
where ......
order by tm.agency_nbr
, pc.customer_id
, p.project_code
, e.bill_rate_id
;
type hrs_tab is table of hrs_cur%rowtype;
hrs_tab hrs_tab_type;
begin
open hrs_cur;
loop
fetch hrs_cur bulk collect into hrs_tab LIMIT 100;
<forall distinct hrs_tab(i).customer_ids , I would like to a FORALL
insert into
invoice (invoice_id, customer_id)
values (invoice_id_seq.nextval , customer_id)
returning invoice_id bulk collect into Invoice_id_array. >
exit when hrs_cur%NOTFOUND;
end loop;
close hrs_cur;
end ;
Appreciate your help.
October 13, 2005 - 10:07 am UTC
this is going to be problematic on many fronts.
First, you'll get an implementation restriction trying to access hrs_tab(i).customer_id in the forall.
Second, you would have to use a SET operation on a collection to distinct the collection for insertion - that would preclude the use of bulk collect on the insert.
Third, you would need an "where not exists" or "where not in" in the set operation as well - since customer_id = 100 could be in the first set of fetched rows AND the second set of fetched rows AND the third set of fetched rows.
Begs the question - why not just insert into select distinct in the first place and not even consider doing this procedurally?
FORALL insert
Anne, October 14, 2005 - 11:41 am UTC
Thanks for your comments, Tom. Yes, you are right, I would need an "where not exists" in the set .... I didn't want to do insert into select distinct just to avoid another query because I have some processing for the detail rows in the cursor - but you're right I'd be better off doing the simple insert into distinct .
Could you explain what the implementation restriction is in trying to access hrs_tab(i).customer_id in the forall ?
October 14, 2005 - 5:25 pm UTC
the implementation restriction is that it won't work.
ops$tkyte@ORA10GR1>
ops$tkyte@ORA10GR1> declare
2 cursor c is select * from emp;
3 type array is table of c%rowtype index by binary_integer;
4 l_data array;
5 begin
6 open c;
7 fetch c bulk collect into l_data;
8 close c;
9 forall i in 1 .. l_data.count
10 insert into t (x) values ( l_data(i).empno );
11 end;
12 /
insert into t (x) values ( l_data(i).empno );
*
ERROR at line 10:
ORA-06550: line 10, column 32:
PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND
table of records
returning clause of update statement
Ravi Kumar, November 09, 2005 - 7:41 am UTC
Hi Tom..
Many thanks for your HELP.
I have a situation where I need to update a row and also need to read the data from the record. I know we can do it with RETURNING clause.. like this
UPDATE emp
SET sal = vin_value
WHERE empno = 7902
RETURNING sal INTO vout_value;
But this gives me the value of SAL after change, But I need to get the value before change.
Is there any way to get that.
Thanks & Regards
Ravi
November 11, 2005 - 9:53 am UTC
you would have to read that out via a select before doing the update.
JUnk record elimination during Load
Girish, February 13, 2007 - 3:41 am UTC
Hi Tom,
We have our ETL designed in such a way that it takes data from data feeds and inserts into various fields and it is scheduled as cron and whenever we get junk record with length greater than size of column we get error and ETL gets terminated.
So is there a way in Oracle to just skip junk record and proceed ,but we are in a position where we can't change all codes because its huge and now we need some solution which we can implement at Table level
Thanks & regards,
Girish
Fetch returning records
Bali, March 07, 2007 - 8:42 am UTC
Hello Tom,
We have a situation (ProC) like the one on March 15, 2005. We want to use returning clause instead of SELECT FOR UPDATE - UPDATE - SELECT. The number of affected records can be between 0 and ~10-15000. With the old way we used cursor. However how we can solve it with the returning clause without creating a huge array?
Thanks in advance!
March 07, 2007 - 11:03 am UTC
if you have 15,000 records to return - and you use returning - well, you would have to have an array that large.
if you stick with select for update, and then bulk update N records, you would of course limit your exposure of rows to N at a time
Rajasekar, April 02, 2007 - 11:10 am UTC
Thanks for the excellent site.
Please let me know if the Pipelined function would be effective in the below case.
I have a external table like below with around 10Million records. Flag Column stores all the flag code related to the album as a singel record. While I load into the target table each code should go in as seperate record as shown below.
Create table my_music
(album_id NUMBER,
flag VARCHAR2(10)
);
INSERT INTO my_music values(1,'AB');
INSERT INTO my_music values(2,'MKCS');
INSERT INTO my_music values(3,'SEWRT');
SQL> select * from my_music;
ALBUM_ID FLAG
---------- ----------
1 AB
2 MKCS
3 SEWRT
The above records should be loaded into target as
1 A
1 B
2 M
2 K
2 C
2 S
3 s
3 E
3 W
3 R
3 T
Can I use pipelined function here? Will that be the optimal way to load. If yes, can you please show here how to implement it?
Or is there anyother better solution to implement.
Thanks,
Raj
April 03, 2007 - 8:52 pm UTC
ops$tkyte%ORA10GR2> with data
2 as
3 (select level l from dual connect by level <= 10)
4 select album_id, substr(flag,l,1) v
5 from my_music, data
6 where length(flag) >= l
7 order by album_id, v
8 /
ALBUM_ID V
---------- -
1 A
1 B
2 C
2 K
2 M
2 S
3 E
3 R
3 S
3 T
3 W
11 rows selected.
Sorry for posting the above query in the wrong place
Rajasekar, April 03, 2007 - 2:09 am UTC
Excellent Solution
Rajasekar, April 04, 2007 - 2:28 am UTC
The solution was most useful and the data load was pretty fast.
Returning does't work with DELETE
Mukund, August 05, 2009 - 5:44 pm UTC
Hi,
I tried the following code:
declare
v_count NUMBER:=0;
type v_type is table of hlrc_clients.clnt_client_id%TYPE;
v_client_id v_type:=v_type();
begin
delete from hlrc_clients
where last_updated_by = user
and rownum <= 100
returning clnt_client_id bulk collect into v_client_id;
v_count:= SQL%ROWCOUNT;
dbms_output.put_line(v_client_id.count);
dbms_output.put_line(v_count);
exception
when others then
dbms_output.put_line('EXCEPTION' || sqlcode || sqlerrm);
dbms_output.put_line('EXCEPTION' || v_client_id.count);
dbms_output.put_line('EXCEPTION' || v_count);
end;
when there is an exception ie. when a delete cannot be performed on any row...the control comes to the EXCEPTION BLOCK
and the output gives the exception and sql%rowcount gives 0 but the collection count gives a different number.
August 05, 2009 - 6:07 pm UTC
exception
when others then
dbms_output.put_line('EXCEPTION' || sqlcode || sqlerrm);
dbms_output.put_line('EXCEPTION' || v_client_id.count);
dbms_output.put_line('EXCEPTION' || v_count);
end;
gag, sputter, cough, cry, so sad. I'll never ever understand WHY anyone does that. Never. Not in a billion years ever.
But, anyway, it is working as designed. SQL statements are by design atomic - they either entirely succeed or entirely fail. The delete either deletes perfectly every row - or not.
You might be interested in DML error logging in this case, you can have errors silently logged to a TABLE that you can query afterwards to see what went wrong.
http://asktom.oracle.com/Misc/how-cool-is-this.html
RETURNING INTO CLAUSE
Mukund, August 05, 2009 - 6:27 pm UTC
Well.. what I fail to understand is:
if RETURNING INTO gives the no. of rows affected then the collection count should be ZERO.The returning clause should return NOTHING. but the collection count is NOT ZERO but indeed all the rows selected for DELETE OPERATION
This is the Output:
EXCEPTION-2292ORA-02292: integrity constraint (MAXEX.CLNT_CLNT_FK) violated - child record found
EXCEPTION500
EXCEPTION0
The first count is the Collection Count
the second count is the SQL%ROWCOUNT
I expected both counts to be ZERO
August 05, 2009 - 6:48 pm UTC
the statement failed - did not happen. The outputs from it are not 'sensible', cannot be used.
It is as if the statement never happened - the things that would be set upon successful completion of the statement - not set, they don't mean anything.
Returning does't work with DELETE
Mukund, August 05, 2009 - 6:40 pm UTC
500 is because:
I modified the query as
rownum <=500
Returning Clause not working!!??
Mukund, August 05, 2009 - 7:15 pm UTC
WEll.. If you say "not sensible & cannot be used"..
"The Exception Handling Mechanism" lost its charm..
Well. .DML Error logging sounds good... but..can you enlighten me as what "RETURNING INTO" MEAN.
According to Documentation if my DML fails.. the values of the variables in the RETURNING CLAUSE should be UNDEFINED
But im getting a definite COUNT when the DML fails.
August 06, 2009 - 8:39 am UTC
why has it lost it's charm?
You hit an exception
variables being processed in the block of code the exception comes from are necessarily "unstable" - you were in the middle of processing them. The statement that raised the exception didn't happen for all intents and purposes - so any side effects it would have had (had it worked) cannot be used.
... RETURNING CLAUSE should be UNDEFINED ...
You should consider them as "undefined" - the numbers, the values you see - garbage, not define, not usable, not useful, not finished, not done, not fully baked - undefined, as if they were uninitialized and had garbage left in them.
I fail to see how this makes exceptions "lose their charm"???? You hit and error, you were teleported to the exception block - the variables that were in the PROCESS of being set are "undefined", their value is "not defined, not reliable, not meaningful, not set". I don't see why you would even consider peeking at them? They are obviously "nothing of use to you" at that point.