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 天的分区进行分类并重建所有索引。这意味着插入没有变化。删除不需要的记录不涉及查询。
我在 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 天的分区进行分类并重建所有索引。这意味着插入没有变化。删除不需要的记录不涉及查询。