Sei sulla pagina 1di 8

LARGE DELETES ON ORACLE TABLES WITHOUT USING INDEXS Author JP Vijaykumar Date September 14th 2011 In one of our

applications, there are some very large tables. These tables are larger than few 100 GBs in size. Most of the operations are inserts and deletes on these tables. The number of ro ws in these tables range from few thousand records to few million records. We planned to re-organize these large tables and reset the high water mark. To test the scenario, I need to create few demo tables, insert data and delete all the data leaving few records. For this demo tables' creation, I was deleting records and wonder, is there a be tter way to delete large number of records from the demo table without using an index ? The details follow: ------------------------------------------------------------------------------------------------------------------------------------------------------------------------connect /as sysdba drop table temp_jp; Create table temp_jp(col1 number, col2 varchar2(10)) tablespace users; set serverout on size 1000000 timing on declare v_num pls_integer:=0; begin for i in 1..1000000 loop v_num:=v_num+1; insert into temp_jp values(i,'ram'); if (v_num>=10000) then commit; v_num:=0; end if; end loop; commit; end; / PL/SQL procedure successfully completed. Elapsed: 00:02:34.50 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------01 Straight delete on the table using an index. drop table temp_jp1; create table temp_jp1 tablespace users nologging parallel(degree 4) as select * from temp_jp;

create index temp_jp1_idx01 on temp_jp1(col1) tablespace users nologging paralle l(degree 4); ------------------------------------------------------------------------------------sqlplus "/as sysdba" alter system flush shared_pool; alter system flush buffer_cache; set timing on delete from temp_jp1 where col1>=101; 999900 rows deleted. Elapsed: 00:01:06.50 select n.name,s.value from v$sesstat s, v$statname n where s.statistic# = n.statistic# and s.sid = (select distinct sid from v$mystat) and n.name in ('CPU used by this session','consistent gets', 'physical reads','index fast full scans (full)'); NAME VALUE ---------------------------------------------------------------- ---------CPU used by this session 6125 consistent gets 3026 physical reads 2076 index fast full scans (full) 0 Elapsed: 00:00:00.05 This operation is efficient and quick. Remember, the overhead, if a few million records are deleted on a large table in a IO intensive OLTP application. Also you need to redo everything, if the operatio n fails with a 1555 error in between. During the large delete operation the table gets locked. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------02 Delete from the table using a cursor loop using an index. Intermittently commit every 1000 deletions. drop table temp_jp1; create table temp_jp1 tablespace users nologging parallel(degree 4) as select * from temp_jp; create index temp_jp1_idx01 on temp_jp1(col1) tablespace users nologging paralle l(degree 4); ------------------------------------------------------------------------------------sqlplus "/as sysdba" alter system flush shared_pool; alter system flush buffer_cache;

alter session set events '10046 trace name context forever, level 12'; set serverout on size 1000000 timing on declare --v_num number; --v_num binary_integer; v_num pls_integer:=0; v_stat char(3); --update stop_jp set status='no'; --commit; begin execute immediate 'create table stop_jp as select ''' 'YES' ''' status from du al'; for c1 in (select col1 from temp_jp1 where col1 >=101 ) loop begin v_num:=v_num + 1; execute immediate 'delete from TEMP_JP1 where col1 = :b1' using c1.col1; --execute immediate 'select col1 from TEMP_JP1 where rowid = ''' c1.row_id ''' ' into v_num; --execute immediate 'select col1 from TEMP_JP1 where rowid = :b1' into v_num us ing c1.row_id; --dbms_output.put_line(c1.row_id ' ' v_num); if (v_num >=1000) then commit; v_num:=0; execute immediate 'select upper(status) from stop_jp' into v_stat; if (v_stat = 'NO') then dbms_output.put_line('JOB WAS ABORTED'); exit; end if; end if; exception when others then dbms_output.put_line(c1.col1 ' ' sqlerrm); end; end loop; execute immediate 'drop table stop_jp'; end ; / PL/SQL procedure successfully completed. Elapsed: 00:05:59.49 alter session set events '10046 trace name context off'; tkprof prod1_ora_6605.trc jp1.dat sys=no waits=yes explain=jp/jp sort=fchela Partial details from jp1.dat file call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------Parse 1 0.10 0.09 8 28 0 0 Execute 1 152.70 153.45 0 0 0 1 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------total 2 152.80 153.55 8 28 0 1 Elapsed times include waiting on following events:

Event waited on ---------------------------------------db file sequential read control file sequential read direct path write db file scattered read log file switch completion undo segment extension buffer busy waits latch: In memory undo latch log file switch (checkpoint incomplete) reliable message enq: RO - fast object reuse

Times Max. Wait Total Waited Waited ---------- -----------2728 0.00 0.15 5 0.00 0.00 1 0.00 0.00 157 0.00 0.04 19 0.98 6.12 9 0.00 0.00 1 0.00 0.00 5 0.00 0.00 27 0.98 10.96 1 0.00 0.00 1 0.00 0.00

select n.name,s.value from v$sesstat s, v$statname n where s.statistic# = n.statistic# and s.sid = (select distinct sid from v$mystat) and n.name in ('CPU used by this session','consistent gets', 'physical reads','index fast full scans (full)'); NAME VALUE ---------------------------------------------------------------- ---------CPU used by this session 35089 consistent gets 3014391 physical reads 4660 index fast full scans (full) 0 Elapsed: 00:00:00.09 ------------------------------------------------------------------------------------Imagine, how much time it takes for the above two operations in step 01 and 02 without using indexes. ------------------------------------------------------------------------------------Since most of the tables, I work on are very large, creating indexes on these large tables, require more freespace in the tablespaces. Here I wondered, can I delete large number of records from these tables without using indexes. I just don't want to run any command like the following sql on a large table: delete from temp_jp where ..; The above sql may fail with the 1555 error. I just wanted to figure out a way to delete large number of records from our demo tables, without the using indexes in a better way. This is for academic purpose only. Use these methods at your own risk and responsibility. I tried couple of variations, with no luck: ------------------------------------------------------------------------------------------------------------------------------------------------------------------------03 deletion based on rownum(not successful):

SQL> select col1 from temp_jp1 a where NOT EXISTS (select rownum from temp_jp1 where rownum <101 and rownum = a.rownum); 2 3 4 and rownum = a.rownum) * ERROR at line 4: ORA-01747: invalid user.table.column, table.column, or column specification Elapsed: 00:00:00.01 select count(1) from (select rownum row_num,col1 from temp_jp1) a where NOT EXISTS (select row_num from (select rownum row_num from temp_jp1 where rownum < 101) b where b.row_num = a.row_num) 2 3 4 5 ; COUNT(1) ---------999900 Elapsed: 00:00:01.48

SQL> delete from (select rownum row_num,t.* from (select rownum from temp_jp1 ) t) where row_num>=101; 2 3 delete from (select rownum row_num,t.* from (select * ERROR at line 1: ORA-01732: data manipulation operation not legal on this view Why I can not use rownum of a table to perform delets? Reference: http://desaitaral.wordpress.com/2010/11/11/ora-01732-data-manipulation-operation -not-legal-on-this-view/ http://orafaq.com/wiki/Pseudo-column ------------------------------------------------------------------------------------------------------------------------------------------------------------------------04 deletion using the rowid: select rowid row_id from temp_jp a where NOT EXISTS (select 'x' from temp_jp b where rownum <10 and a.rowid = b.rowid) 2 4 ; no rows selected Elapsed: 00:00:03.05 ------------------------------------------------------------------------------------select a.rowid from (select rowid from temp_jp ) a,

(select rowid from temp_jp where rownum<10) b where a.rowid <> b.rowid; 2 3 4 where a.rowid <> b.rowid * ERROR at line 4: ORA-01446: cannot select ROWID from, or sample, a view with DISTINCT, GROUP BY, etc. Elapsed: 00:00:00.01 ------------------------------------------------------------------------------------select count(1) from ( select a.row_id from (select /*+ NOPARALLEL(a) FULL(a) */ rowid row_id from temp_jp ) a where NOT EXISTS (select 'x' from 2 3 4 5 (select rowid row_id from temp _jp where rownum < 101) b 6 where a.row_id = b.row_id) ); 7 COUNT(1) ---------999900 Elapsed: 00:00:00.15 ------------------------------------------------------------------------------------alter system flush shared_pool; alter system flush buffer_cache; alter session set events '10046 trace name context forever, level 12'; set serverout on size 1000000 timing on declare --v_num number; v_num binary_integer:=0; --v_num pls_integer:=0; v_stat char(3); --update stop_jp set status='no'; --commit; begin execute immediate 'create table stop_jp as select ''' 'YES' ''' status from du al'; for c1 in (select a.row_id from (select rowid row_id from TEMP_JP1 ) a where NOT EXISTS (select 'x' from (select rowid row_id from TEMP_JP1 where rownum < 101) b where a.row_id = b.row_id ) ) loop begin v_num:=v_num + 1; execute immediate 'delete from TEMP_JP1 where rowid = :b1' using c1.row_id; --execute immediate 'select col1 from TEMP_JP1 where rowid = ''' c1.row_id '''

' into v_num; --execute immediate 'select col1 from TEMP_JP1 where rowid = :b1' into v_num us ing c1.row_id; --dbms_output.put_line(c1.row_id ' ' v_num); if (v_num >=1000) then commit; v_num:=0; execute immediate 'select upper(status) from stop_jp' into v_stat; if (v_stat = 'NO') then dbms_output.put_line('JOB WAS ABORTED'); exit; end if; end if; exception when others then dbms_output.put_line(c1.row_id ' ' sqlerrm); end; end loop; execute immediate 'drop table stop_jp'; end ; / PL/SQL procedure successfully completed. Elapsed: 00:03:08.89 alter session set events '10046 trace name context off'; tkprof prod1_ora_1838.trc jp2.dat sys=no waits=yes explain=jp sort=fchela Partial details from jp2.dat file call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------Parse 1 0.03 0.02 0 0 0 0 Execute 1 0.00 0.00 0 0 0 1 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------total 2 0.03 0.02 0 0 0 1 Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- -----------db file sequential read 321 0.00 0.01 control file sequential read 5 0.00 0.00 direct path write 1 0.00 0.00 PX Deq: Join ACK 4 0.00 0.00 PX Deq Credit: send blkd 4 0.00 0.00 PX Deq: Parse Reply 9 0.02 0.05 os thread startup 5 0.13 0.54 PX Deq: Execute Reply 21 0.00 0.02 PX Deq: Signal ACK 9 0.01 0.01 log file switch completion 15 0.97 1.98 undo segment extension 5 0.00 0.00 buffer busy waits 2 0.00 0.00 latch: cache buffers chains 8 0.00 0.00 latch: In memory undo latch 2 0.00 0.00 log file switch (checkpoint incomplete) 15 0.98 8.07 PX Deq: Table Q Normal 2 0.00 0.00 enq: PS - contention 5 0.00 0.00

reliable message enq: RO - fast object reuse

1 1

0.00 0.00

0.00 0.00

select n.name,s.value from v$sesstat s, v$statname n where s.statistic# = n.statistic# and s.sid = (select distinct sid from v$mystat) and n.name in ('CPU used by this session','consistent gets', 'physical reads','index fast full scans (full)'); NAME VALUE ---------------------------------------------------------------- ---------CPU used by this session 19813 consistent gets 29491 physical reads 8553 index fast full scans (full) 0 Elapsed: 00:00:00.04 This method of delete using rowid seems to be impressive. I had given the timings and the tkprof output. I could delete records from the table using rowids, but could not delete records from the table usig rownum method. Why? I leave this analysis/research for the readers imagination and testing. Happy scripting. References: http://www.orafaq.com/wiki/PLS_INTEGER http://www.scribd.com/doc/20887792/Debugging-Oracle-Rowid

Potrebbero piacerti anche