问题描述
我在Oracle中有一张表,每天填充约一个。 35万条记录。我创建了一个过程,该过程仅在表中保留2个日期,即当有插入第三个不同日期的记录时,它将删除具有最小日期的记录。
以下解决方案有效,但是执行时间太长,因为表中有1M条记录:
CREATE OR REPLACE PROCEDURE DELETE_PREV_DT
AS
nCount NUMBER;
tablename varchar2(50);
BEGIN
FOR aRow IN (SELECT *
FROM TTTAAAA
)
LOOP
tablename := 'TTTAAAA';
EXECUTE IMMEDIATE 'SELECT COUNT(disTINCT DATE_ACCUMULATED) FROM ' || tablename
INTO nCount;
IF nCount > 2 THEN
EXECUTE IMMEDIATE 'DELETE FROM ' || tablename ||
' WHERE DATE_ACCUMULATED = (SELECT MIN(DATE_ACCUMULATED) ' ||
'FROM ' || tablename || ')';
END IF;
END LOOP;
END;
/
有人可以提供修改以便此过程可以更快地运行吗?
解决方法
由于动态SQL,逐行承诺会变慢,并且会进行上下文切换。
这种方法怎么样?对日期进行排序,删除不在前2个中的日期。
SQL> select * from test order by datum,id;
ID DATUM
---------- ----------
1 21.08.2020
2 21.08.2020
3 21.08.2020
4 22.08.2020
5 22.08.2020
6 23.08.2020
7 23.08.2020
8 24.08.2020
8 rows selected.
SQL> delete from test t
2 where t.datum in (select x.datum
3 from (select a.datum,4 dense_rank() over (order by a.datum desc) rn
5 from test a
6 ) x
7 where x.rn > 2
8 );
5 rows deleted.
SQL> select * from test order by datum,id;
ID DATUM
---------- ----------
6 23.08.2020
7 23.08.2020
8 24.08.2020
SQL>
,
@Littlefoot的答案很好。
我提供了另一种观点。由于要求是基于日期删除行,因此该批处理作业适合计划每天运行一次。
表很大(〜1M行)。因此,每天都会有插入和删除操作。 实际上,这导致表和索引碎片化,从而导致性能下降。通常,这是通过在数据库维护窗口期间例行重建索引来解决的。
基于这些实际考虑,另一种方法可能是截断并重新填充表或重新创建表。
在Oracle中,您可以使用CTAS模式根据查询结果创建表。
CREATE TABLE dest_tab AS SELECT * FROM source_tab;
如果您不想动态创建表,也可以使用APPEND提示进行INSERT。
INSERT /*+ APPEND */ INTO dest_tab SELECT * FROM source_tab;
要重新填充dest_tab,您可以这样做
TRUNCATE TABLE dest_tab;
INSERT /*+ APPEND */ INTO dest_tab SELECT * FROM source_tab;
CTAS或INSERT / * + APPEND * /的优点是,您可以通过添加这些提示/子句来执行PARALLEL读取和PARALLEL写入,以通过使用可用资源(内核,临时空间)来提高批处理作业的性能。和记忆。
保留感兴趣数据的SELECT查询将是:
SELECT
x.c1,x.c2
FROM (SELECT /*+ PARALLEL(a,4) */
a.c1,a.c2,dense_rank() over (order by a.datum desc) rn
FROM test a) x
where x.rn <= 2;
您现在可以使用CTAS:
CREATE TABLE xx AS
SELECT
x.c1,dense_rank() over (order by a.datum desc) rn
FROM test a) x
where x.rn <= 2;
,或者如果您喜欢TRUNCATE / INSERT / * + APPEND * /模式,
TRUNCATE TABLE xx;
INSERT /*+ APPEND PARALLEL */ INTO xx
SELECT
x.c1,dense_rank() over (order by a.datum desc) rn
FROM test a) x
where x.rn <= 2;
COMMIT;
请注意提示中的并行读写。
一旦临时xx表中存在数据,您就可以删除并重新创建原始表,或者再次对它进行TRUNCATE和INSERT / * + APPEND * /。由于这是一个大约100万条记录的大容量插入,因此可以通过删除或禁用目标表上的所有索引和约束并在插入之后重建它们来显着提高性能。
不要忘记使用以下方式为会话启用并行数据操作语言:
ALTER SESSION ENABLE PARALLEL DML;
我已经使用这种技术来填充100M +行的数据集市。如果正确完成,这些并行表构建技术可以将大型表的维护时间从数小时缩短至数分钟。
替代方法
还有另一种方法也可以使用。这将使用Oracle分区表。按星期几对表进行分区。然后,在数据库维护窗口期间,每天一次,截断早于2天的分区并重建所有索引。这意味着插入没有任何变化。无需查询即可删除不必要的记录。