1

我有这样的查询:

 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 条记录。结果 - 删除根本不起作用 - 回滚段大小的问题。我无法增加段的大小。

如何执行?

4

3 回答 3

6

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

但你说

“我无法增加段的大小”

这有点令人惊讶,毕竟现在磁盘很便宜。也许你有一个你害怕接近的愤怒的 DBA?但是您的 DBA 没有履行提供数据库以便维护的职责;坦率地说,拥有一个 VLDB(一亿行表这样计数,即使在 PB 和 zettabytes 的今天)没有足够的 Undo 空间是愚蠢的。

但是,如果您不想让数据中心的mintotaur 留胡子,那么您所能做的就是更改您的代码。这是一种选择。鉴于这种 ...

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

... 返回一百万行,因此尝试从中删除太多行tableA,您可以尝试返回较少行的子查询。出于练习的目的,我假设date1to指定的范围date2是 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 表空间。

于 2013-01-03T18:11:10.120 回答
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_bartableA

alter table foo_bar  rename to tableA

请注意,请确保禁用所有索引。

评论

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

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

使用该nologging参数将减少正在使用的回滚量。您无法恢复使用构建的物化视图nologging

于 2013-01-03T17:04:29.307 回答
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(受影响的行数)。

UPDATED QUERY 如前所述,这不会提供最佳性能,但会解决回滚段问题

希望能帮助到你

于 2013-01-03T17:12:26.740 回答