我有一组表,其中主表有150万行。 其中一个子项也接近100万行。
主表的数据也必须复制到历史表中。 我编写了一个PLSQL脚本来首先删除子行和子行,但这个过程耗时太长,接近12小时。
我使用批量收集来删除数据。
脚本将在每天星期一凌晨3点由Java调度程序调用。
我认为在第一次批量删除(有百万行)之后,这个过程会很快,因为数据库每周只会增加近30k个寄存器。
解决此问题的最佳解决方案是什么?
答案 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个小时删除表的数据,它现在将解决我的问题。