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>