Tags

, , , , , , ,

There was a question on Stack Overflow why does delete take longer than insert.

The question is too broad to be addressed as there could be many reasons and statistics behind the two operations. However, the question was asked very generally. And in general, it is true with most of the RDBMS. A DELETE is slower compared to an INSERT operation.

Let’s get into the details.

The autotrace statistics are good enough to find few reasons straight away.

Setup

SQL> CREATE TABLE t
  2    (A NUMBER
  3    );

Table created.

SQL>

INSERT autotrace statistics

SQL> SET AUTOTRACE ON
SQL> INSERT INTO t(A) SELECT LEVEL FROM dual CONNECT BY LEVEL <=1000;

1000 rows created.


Execution Plan
----------------------------------------------------------
Plan hash value: 1236776825

------------------------------------------------------------------------------
| Id  | Operation                     | Name | Rows  | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | INSERT STATEMENT              |      |     1 |     2   (0)| 00:00:01 |
|   1 |  LOAD TABLE CONVENTIONAL      | T    |       |            |          |
|*  2 |   CONNECT BY WITHOUT FILTERING|      |       |            |          |
|   3 |    FAST DUAL                  |      |     1 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(LEVEL<=1000)


Statistics
----------------------------------------------------------
         43  recursive calls
         63  db block gets
         32  consistent gets
          0  physical reads
      19748  redo size
        857  bytes sent via SQL*Net to client
        864  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
       1000  rows processed
SQL>

DELETE autotrace statistics

SQL> SET AUTOTRACE ON
SQL> DELETE FROM t WHERE ROWNUM <= 1000;

1000 rows deleted.


Execution Plan
----------------------------------------------------------
Plan hash value: 325486485

--------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Cost (%CPU)| Time     |
--------------------------------------------------------------------
|   0 | DELETE STATEMENT    |      |     1 |     3   (0)| 00:00:01 |
|   1 |  DELETE             | T    |       |            |          |
|*  2 |   COUNT STOPKEY     |      |       |            |          |
|   3 |    TABLE ACCESS FULL| T    |     1 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(ROWNUM<=1000)

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)


Statistics
----------------------------------------------------------
          8  recursive calls
       1036  db block gets
         15  consistent gets
          0  physical reads
     253264  redo size
        859  bytes sent via SQL*Net to client
        835  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
       1000  rows processed

SQL>

When you DELETE a row the whole row goes to rollback segments and is also written into redo log.

When you do an INSERT the redo size is quite less compared to a DELETE.

Let’s do a small test, I will use DBMS_UTILITY.get_time to compare the timings. I will do a test first with an index, another without an index.

INSERT and DELETE without index:

SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
  2    l_start NUMBER;
  3    l_loops NUMBER := 100000;
  4  BEGIN
  5
  6    l_start := DBMS_UTILITY.get_time;
  7
  8    FOR i IN 1 .. l_loops
  9    LOOP
 10      INSERT INTO t
 11        (a
 12        ) VALUES
 13        (i
 14        );
 15    END LOOP;
 16
 17    COMMIT;
 18
 19    DBMS_OUTPUT.put_line('Time taken for INSERT =' || (DBMS_UTILITY.get_time - l_start) || ' hsecs');
 20
 21    l_start := DBMS_UTILITY.get_time;
 22
 23    FOR i IN 1 .. l_loops
 24    LOOP
 25      DELETE FROM t WHERE a = i;
 26    END LOOP;
 27
 28    COMMIT;
 29
 30    DBMS_OUTPUT.put_line('Time taken for DELETE =' || (DBMS_UTILITY.get_time - l_start) || ' hsecs');
 31
 32  END;
 33  /
Time taken for INSERT =354 hsecs
Time taken for DELETE =10244 hsecs

PL/SQL procedure successfully completed.

Now, let’ first TRUNCATE the table to set the HIGH WATERMARK back to zero.

SQL> TRUNCATE TABLE t;

Table truncated.

SQL>

Create an index:

SQL> CREATE INDEX a_indx ON t(A);

Index created.

SQL>

Gather table stats:

SQL> EXEC DBMS_STATS.gather_table_stats('LALIT', 't');

PL/SQL procedure successfully completed.

INSERT and DELETE with index:

SQL> DECLARE
  2    l_start NUMBER;
  3    l_loops NUMBER := 100000;
  4  BEGIN
  5
  6    l_start := DBMS_UTILITY.get_time;
  7
  8    FOR i IN 1 .. l_loops
  9    LOOP
 10      INSERT INTO t
 11        (a
 12        ) VALUES
 13        (i
 14        );
 15    END LOOP;
 16
 17    COMMIT;
 18
 19    DBMS_OUTPUT.put_line('Time taken for INSERT =' || (DBMS_UTILITY.get_time - l_start) || ' hsecs');
 20
 21    l_start := DBMS_UTILITY.get_time;
 22
 23    FOR i IN 1 .. l_loops
 24    LOOP
 25      DELETE FROM t WHERE a = i;
 26    END LOOP;
 27
 28    COMMIT;
 29
 30    DBMS_OUTPUT.put_line('Time taken for DELETE =' || (DBMS_UTILITY.get_time - l_start) || ' hsecs');
 31
 32  END;
 33  /
Time taken for INSERT =1112 hsecs
Time taken for DELETE =1474 hsecs

PL/SQL procedure successfully completed.

SQL>

So, in either case, DELETE operation takes longer than INSERT.

Hope it helps!