从ORACLE数据库中删除100 mil行,最好的方法是什么?

jutyujz0  于 2023-11-17  发布在  Oracle
关注(0)|答案(2)|浏览(146)

我们有一个巨大的数据库清理行动即将到来,将有大约1亿行删除总数。有40个表删除数据。这里是我的想法,我愿意接受建议

1方法

大容量收集,然后删除,同时记录删除的行。每100行提交一次
示例->

--define record and needed variables

commit_counter NUMBER := 0;
COMMIT_LIMIT CONSTANT NUMBER := 100; 
v_total_deleted_services NUMBER := 0;
TYPE t_record_entity_test IS RECORD (
    ENTITY_ID NUMBER,
    SOURCE VARCHAR2(100),
    SOURCE_ID VARCHAR2(100),
    MESSAGE_ID VARCHAR2(100),
    STATUS VARCHAR2(200)
);

 TYPE t_record_entity_tests IS TABLE OF t_record_entity_test INDEX BY PLS_INTEGER;
 v_records_test t_record_entity_tests;

个字符

2方法

批量收集并记录哪些行被删除。一次删除所有行,然后在最后提交。不知道是否可以,因为其中一个操作可能会删除1000万行

--define record and needed variables

v_total_deleted_services NUMBER := 0;
TYPE t_record_entity_test IS RECORD (
    ENTITY_ID NUMBER,
    SOURCE VARCHAR2(100),
    SOURCE_ID VARCHAR2(100),
    MESSAGE_ID VARCHAR2(100),
    STATUS VARCHAR2(200)
);

 TYPE t_record_entity_tests IS TABLE OF t_record_entity_test INDEX BY PLS_INTEGER;
 v_records_test t_record_entity_tests;
//Make cursor
 CURSOR c_services IS
        SELECT --all the data needed--

  OPEN c_services;
    LOOP
        FETCH c_services BULK COLLECT INTO v_records_test LIMIT 10000; 
        EXIT WHEN v_records_test.COUNT = 0;

   FORALL i IN 1..v_records_test.COUNT
            INSERT INTO DELETE_LOG_TEST(SOURCE, SOURCE_ID, status, log_date)
            VALUES (v_records_test(i).SOURCE, v_records_test(i).SOURCE_ID, 'Service DELETED,' || ' Status: ' ||v_records_test(i).status , SYSDATE);
    end loop;
close c_services;

            DELETE FROM SERVICE WHERE ENTITY_ID = --select entity_id of data needed to be deleted that is the same data that's in the cursor;
    
            v_total_deleted_services := v_total_deleted_services + SQL%ROWCOUNT;


   
commit;
--log number of deleted rows

的字符串
什么是更好的方法,是否有第三种方法比这两种方法更好?

qij5mzcb

qij5mzcb1#

如果您可以在应用程序停机时执行此操作,并且您正在删除一个大表的重要部分,那么使用您想要保留的行创建一个新段比删除您不想要保留的行更有效。最有效的是CTAS和替换:

CREATE TABLE abc$new PARALLEL (DEGREE 16) AS SELECT * FROM abc WHERE [rows-I-want-to-keep];
ALTER TABLE abc RENAME TO abc$old;
ALTER TABLE abc$new RENAME TO abc;

字符串
缺点是你还必须编写脚本并重新应用任何从属对象,如索引,约束,触发器和赠款。但这是完成任务的最有效方法(最少的处理时间)。
另一种方法效率稍低,但在处理从属对象时不太需要注意,它使用相同的概念,但移动数据两次,因此原始对象仍然是永久对象:

CREATE TABLE abc$old NOLOGGING PARALLEL (DEGREE 16) AS SELECT * FROM abc;

TRUNCATE TABLE abc;

ALTER SESSION ENABLE PARALLEL DML;

INSERT /*+ APPEND PARALLEL(abc,16) */ INTO abc SELECT * FROM abc$old WHERE [rows-I-want-to-keep];

COMMIT;


这里的缺点是表会暂时空着,所以你的应用最好关闭。这两种技术都会导致一个表不再包含你想要删除的行,而另一个表包含原始内容,以防你需要恢复。然后,你可以计划在确定不需要数据后删除abc$old表,这样你就可以释放空间。
当然,如果你必须在应用程序使用这些表的同时在线执行这些维护操作,那么这种需求将迫使你使用某种渐进的批量删除过程,比如你正在考虑的那种。这将慢得多,但侵入性较小。

efzxgjgh

efzxgjgh2#

你没有处理任何异常。你确定所有行的delete操作都正常吗?例如,外键约束呢?
总之:如果你一行一行地做,它是慢慢地,它肯定会花时间删除1000万行(如你所说)。如果你切换到设置处理,它可以更快-而不是forall-使用table函数。类似于这样的:
日志表:

SQL> create table delete_log (empno number, log_date date);

Table created.

字符串
示例表;应该删除deptno <> 30的行:

SQL> select * From test order by deptno, ename;

     EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
      7782 CLARK      MANAGER         7839 09.06.1981 00:00:00       2450                    10
      7839 KING       PRESIDENT            17.11.1981 00:00:00       5000                    10
      7934 MILLER     CLERK           7782 23.01.1982 00:00:00       1300                    10
      7876 ADAMS      CLERK           7788 12.01.1983 00:00:00       1100                    20
      7902 FORD       ANALYST         7566 03.12.1981 00:00:00       3000                    20
      7566 JONES      MANAGER         7839 02.04.1981 00:00:00       2975                    20
      7788 SCOTT      ANALYST         7566 09.12.1982 00:00:00       3000                    20
      7369 SMITH      CLERK           7902 17.12.1980 00:00:00        800                    20
      7499 ALLEN      SALESMAN        7698 20.02.1981 00:00:00       1600        300         30
      7698 BLAKE      MANAGER         7839 01.05.1981 00:00:00       2850                    30
      7900 JAMES      CLERK           7698 03.12.1981 00:00:00        950                    30
      7654 MARTIN     SALESMAN        7698 28.09.1981 00:00:00       1250       1400         30
      7844 TURNER     SALESMAN        7698 08.09.1981 00:00:00       1500          0         30
      7521 WARD       SALESMAN        7698 22.02.1981 00:00:00       1250        500         30

14 rows selected.


程序:

SQL> declare
  2    l_tab sys.odcinumberlist;
  3    l_tot number := 0;
  4    cursor c1 is select empno from test where deptno <> 30;
  5  begin
  6    open c1;
  7    loop
  8      fetch c1 bulk collect into l_tab limit 3;
  9      exit when l_tab.count = 0;
 10
 11      insert into delete_log (empno, log_date)
 12        select column_value, sysdate
 13        from table(l_tab);
 14
 15      delete from test t
 16        where exists (select null from table(l_tab)
 17                      where column_value = t.empno);
 18
 19      l_tot := l_tot + sql%rowcount;
 20    end loop;
 21    dbms_output.put_line('Deleted ' || l_tot || ' rows');
 22  end;
 23  /
Deleted 8 rows

PL/SQL procedure successfully completed.


测试结果:

SQL> select * From test order by deptno, ename;

     EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
      7499 ALLEN      SALESMAN        7698 20.02.1981 00:00:00       1600        300         30
      7698 BLAKE      MANAGER         7839 01.05.1981 00:00:00       2850                    30
      7900 JAMES      CLERK           7698 03.12.1981 00:00:00        950                    30
      7654 MARTIN     SALESMAN        7698 28.09.1981 00:00:00       1250       1400         30
      7844 TURNER     SALESMAN        7698 08.09.1981 00:00:00       1500          0         30
      7521 WARD       SALESMAN        7698 22.02.1981 00:00:00       1250        500         30

6 rows selected.


日志:

SQL> select * From delete_log;

     EMPNO LOG_DATE
---------- -------------------
      7782 16.11.2023 11:40:45
      7788 16.11.2023 11:40:45
      7839 16.11.2023 11:40:45
      7876 16.11.2023 11:40:45
      7902 16.11.2023 11:40:45
      7934 16.11.2023 11:40:45
      7369 16.11.2023 11:40:45
      7566 16.11.2023 11:40:45

8 rows selected.

SQL>


至于提交(我没有在这里实现;你知道怎么做):100(在我看来)太低了;设置它为10000(等于你在fetch中使用的限制)。
提醒您:考虑异常处理(如有必要)。

相关问题