Oracle过程优化

Oracle procedure optimization

我在 Oracle 中有一个 table,它每天填充大约。 350k 条记录。我创建了一个程序,只在 table 中保留 2 个日期,当插入第三个不同日期的记录时,它将删除具有最小日期的记录。

下面的解决方案有效,但执行时间太长,因为 table:

中有 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;
/

有人可以提供修改,使这个程序可以 运行 更快吗?

Row-by-row 承诺 slow-by-slow,以及由于动态 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 的回答很好

我提供了一个额外的视角。由于要求是根据日期删除行,所以这个批处理作业是 suitable 调度为 运行 每天一次。

table 很大(~1M 行)。所以,每天都会有插入和删除。 实际上,这会导致 table 和导致性能下降的索引碎片。通常,这是通过在数据库维护期间定期重建索引来处理的 window.

鉴于这些实际考虑,另一种方法可能是 运行 分类和 re-populate table 或 re-create table。

在 Oracle 中,您可以使用 CTAS 模式根据查询结果创建 table。

CREATE TABLE dest_tab AS SELECT * FROM source_tab; 

如果您不想动态创建 tables,您也可以使用 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 */ 的优点是您可以通过添加那些 hints/clauses 来执行并行读取和并行写入,以通过使用可用资源来提高批处理作业的性能 --核心、温度 space 和内存。

保留感兴趣数据的 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, 
  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;

或者如果您更喜欢 TRUNCATE / INSERT /*+ APPEND */ 模式,

TRUNCATE TABLE xx;
INSERT /*+ APPEND PARALLEL */ INTO xx
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;
COMMIT;

注意提示中的并行读写。

一旦数据存在于临时 xx table 中,您就可以删除并重新创建原始 table,或者再次执行 TRUNCATE 和 INSERT /*+ APPEND */ 到其中。由于这是 ~1M 记录的批量插入,您可以通过删除或禁用目标上的所有索引和约束 table 并在插入后重建它们来显着提高性能。

不要忘记为会话启用并行数据操作语言:

ALTER SESSION ENABLE PARALLEL DML;

我已经使用这种技术填充了超过 1 亿行的数据集市。如果操作得当,这些并行 table 构建技术可以将大型 table 维护工作从几小时缩短到几分钟。


替代方法

还有另一种方法也可以使用。这使用 Oracle 分区表。按星期几对 table 进行分区。然后在数据库维护期间每天一次 window,t运行 对超过 2 天的分区进行分类并重建所有索引。这意味着插入没有变化。删除不需要的记录不涉及查询。