如何在Oracle中优化删除查询(带子查询)?

时间:2013-01-03 16:59:29

标签: sql oracle subquery

我的查询如下:

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

表tableB包含近100,000,000条记录。所以

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

返回1,000,000条记录。结果 - 删除根本不起作用 - 回滚段的大小问题。我无法增加细分市场的规模。

如何执行?

3 个答案:

答案 0 :(得分:6)

删除是回滚(撤消)空间使用方面最昂贵的操作,因为我们必须存储整个已删除的行(而撤消插入语句只需要数据库存储rowid)。解决问题的最简单方法是向UNDO表空间添加更多文件。

但你说

  

“我无法增加细分受众群的规模”

这有点令人惊讶,毕竟,这些天磁盘很便宜。也许你有一个愤怒的DBA,你害怕接近?但是你的DBA没有义务提供数据库,因此可以维护它;坦率地说,拥有一个VLDB(即使在数PB和zettabytes这样的日子里,仍然有一个数十亿行表计数),撤销空间不足也是愚蠢的。

但是,如果您不会在数据中心留下胡说八道,那么您所能做的就是更改您的代码。这是一个选择。鉴于此......

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

...返回100万行,因此尝试从tableA删除太多行,您可以尝试返回较少行的子查询。出于练习的目的,我假设date1date2指定的范围是30天:您需要相应地调整以下代码。

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

这将简单地将选择分成十个三天的块。这样做会花费更长的时间,但它不应该破坏Undo表空间。

答案 1 :(得分:3)

你想要做的是:

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);  

后跟原始表的截断+删除

truncate tableA  
drop tableA

然后将foo_bar重命名为tableA

alter table foo_bar  rename to tableA

请注意,请务必禁用所有索引。

评论

  

我不能放桌子。它一直在使用。

你当然可以。您需要隐藏view后面的表格,这只是一个虚拟功能和/或留出一些维护时间,因为您在插入时无法将数据吹走。现在,更典型的方法是将此数据推送到materialized view,作为fact table运行。这允许您随时修改基表(tableA),而不必担心会损害用户对物化视图的查询。

使用nologging参数将减少正在使用的回滚量。您无法恢复使用nologging

构建的实体化视图

答案 2 :(得分:2)

如果您要填写回滚段,可能是因为您的交易涉及的数据量。

我会分成几个块并提交它们,例如:

 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 
    )

给XXXX你想要的值(10000)或其他什么,然后循环执行这个语句,不要忘记在每次迭代中提交。

您必须循环,直到delete语句返回0(受影响的行数)。

更新查询 注意,这不会提供最佳性能,但会解决回滚段问题

希望有所帮助