简体   繁体   中英

How to optimize delete query (with subquery) in Oracle?

I have query like:

 delete from tableA 
 where tableA.fk in (select id 
                     from tableB 
                     where tableB.column1='somevalue' 
                     and tableB.date between date1 and date2)
   ;

Table tableB contains near 100,000,000 records. So

select id 
from tableB 
where  tableB.column1='somevalue' 
and tableB.date between date1 and date2 

returns near 1,000,000 records. As result - delete doesn't work at all - problem with size of rollback segment. I cannot increase size of segment.

How it can be executed?

Deletions are the most expensive operation in terms of rollback (undo) space usage, because we have to store the whole deleted row (whereas to undo an insert statement simply requires the database to store the rowid). The simplest solution to your problem would be to add more files to the UNDO tablespace.

But you say

"I cannot increase size of segment"

This is slightly surprising, After all, disk is cheap these days. Perhaps you have an angry DBA who you are scared to approach? But your DBA is failing in their duty to provision the database so it can be maintained; frankly, it is stupid to have a VLDB (a hundred million row table counts as such, even in these days of petabytes and zettabytes) with insufficient Undo space.

But if you won't beard the mintotaur in the data centre all you can do is change your code. Here is one option. Given this ...

select id 
from tableB 
where  tableB.column1='somevalue' 
and tableB.date between date1 and date2 

... returns one million rows, and hence tries to delete too many rows from tableA , you could try a sub-query which returns fewer rows. For the purposes on the exercise I am assuming the range specified by date1 to date2 is thirty days: you will need to adjust the following code accordingly.

for i in 1..10 loop
    delete from tableA 
     where tableA.fk in (select id 
                         from tableB 
                         where tableB.column1='somevalue' 
                         and tableB.date between date1 + (3 * (i-1)) and  date1 + (3 * i)
       ;
    commit;
end loop

This will simply split the select into ten three-day chunks. It will take much longer to do it this way, but it shouldn't blow the Undo tablespace.

what you want to do is this:

create table foo_bar  
as select  *  
from tableA   
where tableA.fk not in (select id from tableB where 
 tableB.column1='somevalue' and tableB.date between date1 and date2);  

followed by a truncate + delete of the original table

truncate tableA  
drop tableA

Then rename foo_bar to tableA

alter table foo_bar  rename to tableA

Of note, make sure to disable all indexes.

COMMENTS

I cannot drop table. It is always in use.

You most certainly can. You need to hide the table behind a view which is just a virtual function and/or set aside some maintenance time because you cannot blow data away while you are inserting. Now a more typical approach is to push this data into a materialized view that will operate as a fact table . This allows you to modify the base table (tableA) at any time without fear of hurting the user queries against the materialized view.

Use of the nologging parameter will reduce the amount of rollback being utilized. You cannot recover a materialized view built with nologging

If you are filling the rollback segment it is probably because the amount of data involved in your transaction.

I'd split into several chunks and commit each of them, something like :

 delete from tableA where tableA.fk in (
    select id from (
            select id from tableB where 
                tableB.column1='somevalue' and tableB.date between date1 and date2 and id in (select distinct fk from tableA.fk)

        )
        where rownum < XXXX 
    )

give XXXX the value you want (10000) or whatever, and then loop executing this statement and don't forget to commit in each iteration.

You have to loop until the delete statement returns 0 (number of affected rows).

UPDATED QUERY As noticed, this will not give the best performance, but will solve the rollback segment problem

Hope it helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM