在具有许多关系的表中删除100万行的最佳方法

时间:2015-01-31 10:41:08

标签: sql oracle plsql delete-row

我有一组表,其中主表有150万行。 其中一个子项也接近100万行。

主表的数据也必须复制到历史表中。 我编写了一个PLSQL脚本来首先删除子行和子行,但这个过程耗时太长,接近12小时。

我使用批量收集来删除数据。

脚本将在每天星期一凌晨3点由Java调度程序调用。

我认为在第一次批量删除(有百万行)之后,这个过程会很快,因为数据库每周只会增加近30k个寄存器。

解决此问题的最佳解决方案是什么?

3 个答案:

答案 0 :(得分:6)

通常,从表中“删除”大量行的最快方法是将它们存储在临时表中,截断表并重新插入它们:

create table tempt as
    select *
    from t
    where . . .;

truncate table t;

insert into t
    select *
    from tempt;

这适用于一个表,它不会级联删除。根据触发器的设置方式,数据可能会更改(例如插入时间)。您的问题没有提供足够的信息。

但是,从理论上讲,您可以在子表上继续此过程:

create table tempchild as
    select *
    from child
    where child.parentid in (select id from t);

truncate table child;

insert into child
    select *
    from tempchild;

这是实现这一目标的快速方法的概述。如果您有可能影响不同表中记录之间关系的触发器和约束,则会变得更复杂。

答案 1 :(得分:5)

如果没有解释您要删除的表格,与之相关的表格以及所有这些表格中的索引,就无法说明为什么会删除这些表格花了这么久。有人想到 - 如果你有一个从一个表到另一个表的外键,那么外键关系中涉及的字段应该在两个表上建立索引。例如,我们假设您有以下表格:

PARENT_TABLE (1,000,000 rows)
  ID_PARENT       NUMBER PRIMARY KEY
  SOMETHING       VARCHAR2(10)
  SOMETHING_ELSE  NUMBER
  VALUE_FIELD     VARCHAR2(20) REFERENCES VALID_VALUES(VALUE_FIELD)
  ID_XXX_TABLE    NUMBER REFERENCES XXX_TABLE (ID_XXX_TABLE)

CHILD_TABLE (2,000,000 rows)
  ID_CHILD        NUMBER PRIMARY KEY
  ID_PARENT       NUMBER REFERENCES PARENT_TABLE(ID_PARENT) CASCADE DELETES
  BLAH            VARCHAR2(15)
  BLAH_BLAH       VARCHAR2(100)
  VALUE_FIELD     VARCHAR2(20) REFERENCES VALID_VALUES(VALUE_FIELD)

XXX_TABLE (100,000 rows)
  ID_XXX_TABLE    NUMBER PRIMARY KEY
  FUBAR           NUMBER
  FOOBAR          NUMBER
  VALUE_FIELD     VARCHAR2(20) REFERENCES VALID_VALUES(VALUE_FIELD)

VALID_VALUES (25 rows)
  VALUE_FIELD     VARCHAR2(20) PRIMARY KEY
  GOOD_BAD_UGLY   CHAR(1) CHECK(GOOD_BAD_UGLY IN ('G', 'B', 'U')

在这种情况下,应创建以下索引:

PK_PARENT_TABLE ON PARENT_TABLE (ID_PARENT)
PARENT_TABLE_1  ON PARENT_TABLE (VALUE_FIELD)
PARENT_TABLE_2  ON PARENT_TABLE (ID_XXX_TABLE)

PK_CHILD_TABLE  ON CHILD_TABLE (ID_CHILD)
CHILD_TABLE_1   ON CHILD_TABLE (ID_PARENT)
CHILD_TABLE_2   ON CHILD_TABLE (VALUE_FIELD)

PK_XXX_TABLE    ON XXX_TABLE (ID_XXX_TABLE)
XXX_FIELD_1     ON XXX_TABLE (VALUE_FIELD)

PK_VALID_VALUES ON VALID_VALUES (VALUE_FIELD)

要解释为什么需要创建这些索引,请考虑当您想从其中一些表中删除行时会发生什么:

<强> PARENT_TABLE

DELETE FROM PARENT_TABLE WHERE ID_PARENT = 123

执行此操作时,数据库需要查找引用要删除的行的每一行。只有一个具有外键约束的表引用PARENT_TABLE,即CHILD_TABLE。因此数据库必须执行等效的SELECT * FROM CHILD_TABLE WHERE ID_PARENT = 123。现在,可以始终执行此语句 - 但如果CHILD_TABLE.ID_PARENT未被编入索引,则保证计划将为FULL TABLE SCAN并且必须读取CHILD_TABLE中的每一行。如果CHILD_TABLE有很多行,这将很慢。另一方面,如果CHILD_TABLE在ID_PARENT上有一个索引,它应该是一个直接索引查找,通常非常快。

<强> VALID_VALUES

DELETE FROM VALID_VALUES WHERE VALUE_FIELD = 'ZORTNOBBLE'

看起来很简单 - 只需从VALID_VALUES中删除一行,其中只有25行。应该快点吧?嗯...也许不是。有三个引用VALID_VALUES.VALUE_FIELD的外键约束 - 如果任何引用表缺少VALUE_FIELD上的索引,它将强制读取和检查该表中的每一行,以查看是否存在从VALID_VALUES中删除的值。这很可能是一个非常缓慢的操作。

因此,正如我所说,确保外键约束的BOTH SIDES上的表在每个外键的所有字段上都有一个索引。您可能会发现这会大大改善您的情况。

如果您可以编辑您的问题并包含所涉及的表的定义,包括所有外键约束以及这些表中存在的所有索引,则某人可能能够提供更具体的建议。

祝你好运。

答案 2 :(得分:0)

我做了一些测试,发现桌子本身很慢。 我不能在我正在测试的环境中删除索引,但我认为索引导致性能问题(删除300条记录需要18秒)。

我在程序中添加了一个超时,因此每天只花4个小时删除表的数据,它现在将解决我的问题。