Skip to Main Content
  • Questions
  • convert from char to varchar2 - retrieve space

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Solano.

Asked: January 15, 2018 - 5:46 pm UTC

Last updated: January 16, 2018 - 12:12 am UTC

Version: 11.2.0.4.0

Viewed 10K+ times! This question is

You Asked

Hi Tom;

We are working on Oracle 11g standard edition.
By mistake, several CHAR fields have been created in several large tables, which generated an increase in the space occupied by them.
We convert the fields to VARCHAR2, but we can not recover the space. We even rebuild the tables

Here is an example.
Why is this happening?


Connected to Oracle Database 11g Release 11.2.0.4.0
Connected as srios

SQL> create table srios.test1 (cid NUMBER(16) not null, ctxt VARCHAR2(200));

Table created

SQL> insert into srios.test1
2 select level, rpad('a', trunc(dbms_random.value(1,50)), chr(trunc(dbms_random.value(48,122)))) from DUAL connect by level <=10000;

10000 rows inserted

SQL> commit;

Commit complete

SQL> select * from srios.test1 where rownum<=5;

CID CTXT
----------------- --------------------------------------------------------------------------------
422 ammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
423 appppppppppppppppppppppppppppppppppppppppp
424 aaaaaaaaaaaaaaaa
425 a66666666666666666666666666666666666666666666666
426 annnnnnnnnnnnnnnnnnnnnn

SQL> select bytes/1024 from dba_segments where owner='SRIOS' and segment_name='TEST1';

BYTES/1024
----------
448

SQL> alter table srios.test1 modify ctxt CHAR(200);

Table altered

SQL> select bytes/1024 from dba_segments where owner='SRIOS' and segment_name='TEST1';

BYTES/1024
----------
3072

SQL> alter table srios.test1 modify ctxt VARCHAR2(200);

Table altered

SQL> select bytes/1024 from dba_segments where owner='SRIOS' and segment_name='TEST1';

BYTES/1024
----------
3072

SQL> create table srios.test2 (cid NUMBER(16) not null, ctxt VARCHAR2(200));

Table created

SQL> insert into srios.test2 select * from srios.test1;

10000 rows inserted

SQL> commit;

Commit complete

SQL> select bytes/1024 from dba_segments where owner='SRIOS' and segment_name='TEST2';

BYTES/1024
----------
3072

SQL>


Thank you!

and Connor said...

When we stored your CHAR columns we went ahead and added those spaces to the tail of the columns. So we really have consumed that space. After we convert to VARCHAR2 are STILL consuming that space because we STILL have those spaces there.

SQL> create table t ( x char(100));

Table created.

SQL>
SQL> insert into t
  2  select rownum from dual
  3  connect by level <= 50000;

50000 rows created.

SQL>
SQL> select
  2    min(length(x)),
  3    max(length(x))
  4  from t;

MIN(LENGTH(X)) MAX(LENGTH(X))
-------------- --------------
           100            100

1 row selected.

SQL>
SQL> exec dbms_stats.gather_table_stats('','T');

PL/SQL procedure successfully completed.

SQL> select blocks from user_tables where table_name = 'T';

    BLOCKS
----------
       748

1 row selected.

SQL> alter table t modify x varchar2(100);

Table altered.

SQL>
SQL> select
  2    min(length(x)),
  3    max(length(x))
  4  from t;

MIN(LENGTH(X)) MAX(LENGTH(X))
-------------- --------------
           100            100

1 row selected.

SQL> exec dbms_stats.gather_table_stats('','T');

PL/SQL procedure successfully completed.

SQL> select blocks from user_tables where table_name = 'T';

    BLOCKS
----------
       748

1 row selected.


We can remove the trailing space, but we need to update the rows to do so. That will release the space for new rows in *this* table to use, but it will not release that space back to the database so to speak. The table will still consume the same space

SQL> update t set x = rtrim(x);

50000 rows updated.

SQL> commit;

Commit complete.

SQL>
SQL> select
  2    min(length(x)),
  3    max(length(x))
  4  from t;

MIN(LENGTH(X)) MAX(LENGTH(X))
-------------- --------------
             1              5

1 row selected.

SQL>
SQL> exec dbms_stats.gather_table_stats('','T');

PL/SQL procedure successfully completed.

SQL> select blocks from user_tables where table_name = 'T';

    BLOCKS
----------
       748

1 row selected.


If you want to get this space back, then you need to reorg the table, either with a 'move' or 'shrink space' command. For example

SQL>
SQL> alter table t move;

Table altered.

SQL> exec dbms_stats.gather_table_stats('','T');

PL/SQL procedure successfully completed.

SQL> select blocks from user_tables where table_name = 'T';

    BLOCKS
----------
        85

1 row selected.

SQL>



Is this answer out of date? If it is, please let us know via a Comment

More to Explore

Administration

Need more information on Administration? Check out the Administrators guide for the Oracle Database