[Back To HomePage and Script Library]

Disclaimer: Use these scripts and/or any recommendations they may contain at your own risk. These scripts may or may not have been tested.

Title: Avoiding ORA-1555 Error When Deleting Large Tables

Author: Ujwal Shrivatsa, a Technical Consultant for Database Consultants, Inc. in San
Antonio, Texas.

Deleting large number of rows from a table requires a large enough rollback segment. Where rollback segments are luxury, you
might get the error, ora-1555 "snapshot too old: rollback segment number %s with name \"%s\" too small."

To avoid this problem, the procedure described below commits after every N number of rows specified by the user so that
rollback segments are not taxed.


Source/Text/Comments


-- Ujwal Shrivatsa  September 1998

--This procedure deletes rows from the table specified in tname for the
--condition in where_clause. It commits after every n records specified by the parameter commit_size .
-- eg : execute delete_table('emp','where job=''clerk''',2000);
-- if where_clause and commit_size is not specified the procedure deletes
-- all the records for the table specified commiting after every 1000 rows


CREATE OR REPLACE PROCEDURE delete_table
                                         (tname in varchar2,
                                          where_clause in varchar2 default null,
                                          commit_size in number default 1000) is

  sel_id      INTEGER;
  del_id      INTEGER;
  exec_sel    INTEGER;
  exec_del    INTEGER;
  cur_stmt    VARCHAR2(2000);
  del_rowid   ROWID;
BEGIN
  --Prepare cursor to select rowids of the records to be deleted.
  cur_stmt := 'SELECT rowid FROM '||tname||' '||where_clause;
  sel_id := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(sel_id,cur_stmt,dbms_sql.v7);
  DBMS_SQL.DEFINE_COLUMN_ROWID(sel_id,1,del_rowid);
  exec_sel := DBMS_SQL.EXECUTE(sel_id);
  --Prepare cursor to delete records
  del_id := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(del_id,'delete '||tname||
               ' where rowid = :del_rowid',dbms_sql.v7);
 LOOP
   IF DBMS_SQL.FETCH_ROWS(sel_id) >0 THEN
      --Get the rowid to be deleted
      DBMS_SQL.COLUMN_VALUE(sel_id,1,del_rowid);
      DBMS_SQL.BIND_VARIABLE(del_id,'del_rowid',del_rowid);
      --Execute the delete for the rowid got
      exec_del := DBMS_SQL.EXECUTE(del_id);
      -- Commit for every n records where n is specified in commit_size
      if mod(dbms_sql.last_row_count,commit_size) = 0 then
      COMMIT;
      end if;
   ELSE exit;
   END IF;
 END LOOP;
 -- Commit again to catch the last set of records and close the cursors
  COMMIT;
  DBMS_SQL.CLOSE_CURSOR(sel_id);
  DBMS_SQL.CLOSE_CURSOR(del_id);
END;
/