Good friend Gwen posted this on Twitter yesterday, presumably alluding to some sort of potential issue with Postgres.

image

and it got me thinking: “I wonder how Oracle will go with this”.

So here’s a simple test, showing 5000 repeated ADD and DROP column commands


SQL> create table t ( x1 int, x2 int );

Table created.

SQL> begin
  2    for i in 1 .. 5000 loop
  3      execute immediate 'alter table t add newcol int';
  4      execute immediate 'alter table t drop column newcol';
  5    end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

Now before you think I’m about to jump onto my high horse and bash all the non-Oracle databases out there, we also need to show an additional demo.

In the main, when you want to drop a column, we always recommend “SET UNUSED” because that is a zero-cost option to drop a column. My “DROP COLUMN” demo above is efficient because the table is empty. In a real life scenario, the data in the table must be re-written in order to physically eliminate the column data from each row. Here’s the same demo with the SET UNUSED variant.


SQL> create table t1 ( x1 int, x2 int );

Table created.

SQL> begin
  2    for i in 1 .. 5000 loop
  3      execute immediate 'alter table t1 add newcol int';
  4      execute immediate 'alter table t1 set unused column newcol';
  5    end loop;
  6  end;
  7  /
begin
*
ERROR at line 1:
ORA-01792: maximum number of columns in a table or view is 1000
ORA-06512: at line 3

There is no real surprises here. A “SET UNUSED” command simply tells the dictionary that this column is no longer referenceable, but the data is still there. Hence the data dictionary keeps a hidden column reference for the “dropped” column. Every time I issue “SET UNUSED” I get another hidden column. Eventually, all those hidden columns add up to reach the table limit (1000 columns in 19c and below) and kersplat.

If you ever hit this issue (I think it would be a strange set of circumstances if you did), simply use DBMS_REDEFINITION to clean up the mess, eg


SQL> create table t ( x1 int, x2 int );

Table created.

SQL> insert into t select rownum, rownum from dual connect by level <= 100;

100 rows created.

SQL> alter table t add constraint t_pk primary key ( x1);

Table altered.

SQL> create index t_ix on t ( x2);

Index created.

SQL>
SQL> begin
  2    for i in 1 .. 5000 loop
  3      execute immediate 'alter table t add newcol int';
  4      execute immediate 'alter table t set unused column newcol';
  5    end loop;
  6  end;
  7  /
begin
*
ERROR at line 1:
ORA-01792: maximum number of columns in a table or view is 1000
ORA-06512: at line 3

SQL> select count(*)
  2  from user_tab_cols
  3  where table_name = 'T';

  COUNT(*)
----------
      1000

SQL> exec dbms_redefinition.redef_table(user,'T',table_part_tablespace=>'USERS')

PL/SQL procedure successfully completed.

SQL> select count(*)
  2  from user_tab_cols
  3  where table_name = 'T';

  COUNT(*)
----------
         2

12 responses to “Can you REALLY drop those columns?”

  1. Hemant K Chitale Avatar
    Hemant K Chitale

    “exec dbms_redefinition.redef_table(user,’T’,table_part_tablespace=>’USERS’)”

    Neat trick. I didn’t know that.

  2. jammydoughball Avatar
    jammydoughball

    hi Connor, is MOVE ONLINE better or worse than dbms_redefinition in this example?

    1. Move online will preserve the “dropped” columns

      SQL> create table t( c1 int, c2 int);

      Table created.

      SQL> insert into t values (1,1);

      1 row created.

      SQL> alter table t set unused column c2;

      Table altered.

      SQL> select column_name from user_tab_cols
      2 where table_name = ‘T’;

      COLUMN_NAME
      ——————————
      C1
      SYS_C00002_25042318:28:45$

      SQL> alter table t move online;

      Table altered.

      SQL> select column_name from user_tab_cols
      2 where table_name = ‘T’;

      COLUMN_NAME
      ——————————
      C1
      SYS_C00002_25042318:28:45$

  3. Maris Gabalins Avatar
    Maris Gabalins

    Hmmm, does TRUNCATE preserve dropped cols ?

    1. It will preserve the hidden column *definitions* set to unused, but of course all the data is gone

  4. If the column is truly no longer needed, just set column values to null, empty_clob(), whatever, then hide the column.

    Much time and angst saved.

    1. although you’d still hit the 1000 limit… but as I said in the post, anyone adding/dropping so frequently is worthy of a question as to why

      1. I had in mind some work i will have to do. “Drop” 2 clob columns on a 360M row table.

        Don’t really want to drop those for real.

        1. Assuming the non-clob portion of the table isn’t massive, the dbms_redef call should do that quite nicely.

          1. I have considered dbms_redef. The fact that the table stays online is nice.

            There are 2 CLOB columns that must be converted to blob, and as part of the conversion , those clobs can be set to empty_clob() to avoid space issues.

            in fact, quite a bit of space will be regained, making redef even more attractive.

            But, after the long conversion weekend, hiding the columns might be very attractive.

  5. Clifford Mathew Avatar
    Clifford Mathew

    Does exec dbms_redefinition.redef_table(user,’T’,table_part_tablespace=>’USERS’) clean up the data from files like the DROP COLUMN does? Thanks.

    1. Yes. Its similar to a CTAS

Got some thoughts? Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Trending