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

br1*_*337 2 sql oracle plsql delete-row

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

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

我正在使用批量收集来删除数据.

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

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

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

Gor*_*off 6

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

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

truncate table t;

insert into t
    select *
    from tempt;
Run Code Online (Sandbox Code Playgroud)

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

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

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

truncate table child;

insert into child
    select *
    from tempchild;
Run Code Online (Sandbox Code Playgroud)

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


Bob*_*ica 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')
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

为了解释为什么需要创建这些索引,让我们考虑当你想从这些表中删除一行时会发生什么:

PARENT_TABLE

DELETE FROM PARENT_TABLE WHERE ID_PARENT = 123
Run Code Online (Sandbox Code Playgroud)

执行此操作时,数据库需要查找引用要删除的行的每一行.只有一个表具有引用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'
Run Code Online (Sandbox Code Playgroud)

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

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

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

祝你好运.