從大表之中刪除大量行

CREATE TABLE NEW_TABLE NOLOGGING PARALLEL 4
AS
SELECT OLD_TABLE.* 
FROM OLD_TABLE 
WHERE (condiction)


TRUNCATE TABLE OLD_TABLE;
INSERT INTO OLD_TABLE SELECT * FROM NEW_TABLE;



I am not a database person, exactly, and most of my db work has been with MySQL, so forgive me if something in this question is incredibly naive.

I need to delete 5.5 million rows from an Oracle table that has about 100 million rows. I have all the IDs of the rows I need to delete in a temporary table. If it were a just a few thousand rows, I'd do this: sql

DELETE FROM table_name WHERE id IN (SELECT id FROM temp_table);
COMMIT;



Is there anything I need to be aware of, and/or do differently, because it's 5.5 million rows? I thought about doing a loop, something like this: app

DECLARE
  vCT NUMBER(38) := 0;

BEGIN
  FOR t IN (SELECT id FROM temp_table) LOOP
    DELETE FROM table_name WHERE id = t.id;
    vCT := vCT + 1;
    IF MOD(vCT,200000) = 0 THEN
      COMMIT;
    END IF;
  END LOOP;
  COMMIT;
END;



First of all - is this doing what I think it is - batching commits of 200,000 at a time? Assuming it is, I'm still not sure if it's better to generate 5.5 million SQL statements, and commit in batches of 200,000, or to have one SQL statement and commit all at once. ide

Ideas? Best practices? oop

EDIT: I ran the first option, the single delete statement, and it only took 2 hours to complete in development. Based on that, it's queued to be run in production. Thanks for your input! this

A:The first approach is better, because you give the query optimizer a clear picture of what you are trying to do, instead of trying to hide it. The database engine might take a different approach to deleting 5.5m (or 5.5% of the table) internally than to deleting 200k (or 0.2%). spa

Here is also an article about massive DELETE in Oracle which you might want to read. code


The fastest way is to create a new one with CREATE TABLE AS SELECT using NOLOGGING option. I mean: server

ALTER TABLE table_to_delete RENAME TO tmp;
CREATE TABLE table_to_delete NOLOGGING AS SELECT .... ;



Of course you have to recreate constraints with no validate, indexes with nologging, grants, ... but is very very fast. get

If you have the trouble in production, you can do the following: input

ALTER TABLE table_to_delete RENAME to tmp;
CREATE VIEW table_to_delete AS SELECT * FROM tmp;
-- Until there can be instantly
CREATE TABLE new_table NOLOGGING AS SELECT .... FROM tmp WHERE ...;
<create indexes with nologging>
<create constraints with novalidate>
<create other things...>
-- From here ...
DROP VIEW table_to_delete;
ALTER TABLE new_table RENAME TO table_to_delete;
-- To here, also instantly



You have take care of:

  • Stored procedures can be invalidated, but they will be recompiled the second time are called. You have to test it.
  • NOLOGGING means that minimal redo are generated. If you have DBA role, run a ALTER SYSTEM CHECKPOINT to ensure no data lost if instance crash.
  • For NOLOGGING the tablespace have to be also in NOLOGGING.

Another option better than create milions of inserts is:

-- Create table with ids
DELETE FROM table_to_delete
 WHERE ID in (SELECT ID FROM table_with_ids WHERE ROWNUM < 100000);
DELETE FROM table_with_ids WHERE ROWNUM < 100000;
COMMIT;
-- Run this 50 times ;-)

不少時候,因爲不能中止server,只能採起這種方法,注意要commit,要不回滾太大。


The PLSQL choice is not advisable because can create the Snapshot too old message due that you are commiting (and closing the transaction) with an opened cursor (the looped one) you want to continue using it. Oracle allows it but it's not a good practice.

UPDATE: Why I can ensure the last PLSQL block is going to work? Because I supose that:

  • No other one is using this temporary table for any reason (dba or jobs gathering statistics, dab tasks like move, inserting records, and so on). That can be ensured because is an auxiliar table only for this.
  • Then, with the last assertion, the query is going to be executed exactly with the same plan and is going to return the rows with the same order.
相關文章
相關標籤/搜索