Skip to Main Content
  • Questions
  • Space required in UNDO tablespace for adding a new column with Default value not null.

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Rub.

Asked: January 13, 2017 - 8:43 am UTC

Last updated: January 18, 2017 - 12:28 am UTC

Version: 10.2.0.4

Viewed 1000+ times

You Asked

Hi Tom,

Recently we needed to add a new field in a table with a default value and a not null constraint, since the table was 45 million rows and 7GB, we ran out of space in UNDO tablespace.

In order to avoid the issue in the future, we did some tests with a new table and we got the following results:

- Creating a table with a single field varchar2(50) and inserting 1 million rows with a string of 50 characters. --> 60 MB
- Adding a new field in the table with the same settings (varchar2(50) with a default value of a 50 characters string and a not null constraint.
- Once the field was added to the table, we checked the difference of UNDO extents consumed and we saw that it required more than 200MB.
- Another test attempted was to perform this operation in three steps:
- Adding the column without default value.
- Update the table setting the default value for all the rows.
- Adding the not null constraint.
This test was much faster, but again it consumed even more UNDO extents that our previous test.

Right now our question is: Is there any way to calculate the amount of UNDO needed for that kind of operations? Based in our previous knowledge, it shouldn't be larger than the size of the table after the operation.

We already know that this kind of DDL are much smoother in 11g, but we're afraid we'll face the same kind of issues before we are able to migrate to 11g.

Thanks in advance for your comments.

and Connor said...

In either case, there is a large potential issue left hanging -if the rows have grown to such an extent that they no longer fit back in their current block, you've now got a giant row migration mess which could be a performance problem.

To fix that... you need to do an alter-table-move, and suddenly, you've got application outages coming out of your ears :-)

How about this for an alternative approach - use DBMS_REDEFINITION with column mapping to populate the new column. Here's an example (I've added some constraints and triggers to show that we can copy all that over as well)


SQL> create table t as select * from scott.emp;

Table created.

SQL>
SQL> alter table t add primary key (empno);

Table altered.

SQL>
SQL> alter table t add constraint CK check ( sal >= 0 );

Table altered.

SQL>
SQL> create or replace
  2  trigger t_TRG
  3  before insert on t
  4  for each row
  5  begin
  6    :new.sal := :new.sal * 10 ;
  7  end;
  8  /

Trigger created.

SQL>
SQL> create table new_t (
  2     EMPNO                                              NUMBER(4)
  3    ,ENAME                                              VARCHAR2(10)
  4    ,JOB                                                VARCHAR2(9)
  5    ,MGR                                                NUMBER(4)
  6    ,HIREDATE                                           DATE
  7    ,SAL                                                NUMBER(7,2)
  8    ,COMM                                               NUMBER(7,2)
  9    ,DEPTNO                                             NUMBER
 10    ,NEW_COL       varchar2(30) default 'Hello' not null  --- Our new column with default not null
 11  );

Table created.

SQL>
SQL> exec dbms_redefinition.can_redef_table(user, 'T');

PL/SQL procedure successfully completed.

SQL>
SQL> exec dbms_redefinition.start_redef_table(user, 'T', 'NEW_T', col_mapping=>'empno,ename,job,mgr,hiredate,sal,comm,deptno,''Hello'' new_col');

PL/SQL procedure successfully completed.

SQL> exec dbms_redefinition.sync_interim_table(user, 'T', 'NEW_T');

PL/SQL procedure successfully completed.

SQL> set serveroutput on
SQL> declare
  2    l_err pls_integer;
  3  begin
  4    dbms_redefinition.copy_table_dependents(
  5      uname               => user,
  6      orig_table          => 'T',
  7      int_table           => 'NEW_T',
  8      num_errors          => l_err
  9      );
 10
 11    dbms_output.put_line('errors=' || l_err);
 12  end;
 13  /
errors=0

PL/SQL procedure successfully completed.

SQL>
SQL> exec dbms_redefinition.finish_redef_table(user, 'T', 'NEW_T');

PL/SQL procedure successfully completed.

SQL>
SQL> desc t
 Name                                                                    Null?    Type
 ----------------------------------------------------------------------- -------- ------------------------------------------------
 EMPNO                                                                   NOT NULL NUMBER(4)
 ENAME                                                                            VARCHAR2(10)
 JOB                                                                              VARCHAR2(9)
 MGR                                                                              NUMBER(4)
 HIREDATE                                                                         DATE
 SAL                                                                              NUMBER(7,2)
 COMM                                                                             NUMBER(7,2)
 DEPTNO                                                                           NUMBER
 NEW_COL                                                                 NOT NULL VARCHAR2(30)

SQL> select empno, ename, new_col from t;

     EMPNO ENAME      NEW_COL
---------- ---------- ------------------------
      7369 SMITH      Hello
      7499 ALLEN      Hello
      7521 WARD       Hello
      7566 JONES      Hello
      7654 MARTIN     Hello
      7698 BLAKE      Hello
      7782 CLARK      Hello
      7788 SCOTT      Hello
      7839 KING       Hello
      7844 TURNER     Hello
      7876 ADAMS      Hello
      7900 JAMES      Hello
      7902 FORD       Hello
      7934 MILLER     Hello

14 rows selected.


Rating

  (4 ratings)

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

Comments

Question different scenario

A reader, January 15, 2017 - 8:51 am UTC

Hi.
I have a huge partioned table with 200 cols.
Now business need complementary of 30 cols with no constraints on them and no need to update existing rows. Just new insertions will so fill as ell the newly added cols.

Will we have same issue regarding the extents and blocks? And should we redefine ? What are impacts of adding as much as 30 columns - some are vc 50 , some number of 20 ..
Connor McDonald
January 17, 2017 - 1:12 am UTC

If you just need to add columns, then this can be done without negative impact. All that happens is the data dictionary is updated, and subsequent inserts can use the new columns.

Its only when you need to update existing rows (directly or indirectly) that you might need to consider reparative action.

adding new columns

Rajeshwaran, Jeyabal, January 16, 2017 - 8:48 am UTC

business need complementary of 30 cols with no constraints on them and no need to update existing rows.

adding new columns with no updates to existing rows(no default values) don't make any row migrations, and it will be really fast (just making entries into data dictionaries).

demo@ORA12C> select count(*) from big_table;

  COUNT(*)
----------
  10000000

1 row selected.

demo@ORA12C> exec show_space('BIG_TABLE');
Unformatted Blocks .....................               0
FS1 Blocks (0-25)  .....................               0
FS2 Blocks (25-50) .....................               0
FS3 Blocks (50-75) .....................               0
FS4 Blocks (75-100).....................              33
Full Blocks        .....................         157,023
Total Blocks............................         158,192
Total Bytes.............................   1,295,908,864
Total MBytes............................           1,235
Unused Blocks...........................               0
Unused Bytes............................               0
Last Used Ext FileId....................               8
Last Used Ext BlockId...................         158,720
Last Used Block.........................               8

PL/SQL procedure successfully completed.

demo@ORA12C> set timing on
demo@ORA12C> alter table big_table add x char(1000);

Table altered.

Elapsed: 00:00:00.03
demo@ORA12C> set timing off
demo@ORA12C>
demo@ORA12C> exec show_space('BIG_TABLE');
Unformatted Blocks .....................               0
FS1 Blocks (0-25)  .....................               0
FS2 Blocks (25-50) .....................               0
FS3 Blocks (50-75) .....................               0
FS4 Blocks (75-100).....................              33
Full Blocks        .....................         157,023
Total Blocks............................         158,192
Total Bytes.............................   1,295,908,864
Total MBytes............................           1,235
Unused Blocks...........................               0
Unused Bytes............................               0
Last Used Ext FileId....................               8
Last Used Ext BlockId...................         158,720
Last Used Block.........................               8

PL/SQL procedure successfully completed.

demo@ORA12C>

good

A reader, January 16, 2017 - 12:48 pm UTC

nice proc show_space (can have the code?)
well true old data will not be updated;
but what about new inserted lines? does the fact that i add (not one char(1000) ) 30 varcher2(1000) - does that encouter a block migration issue?

Connor McDonald
January 17, 2017 - 1:25 am UTC

For varchar2, the length defines the maximum size, not how much space will be used. For example

SQL> create table t ( c1 varchar2(1), c2 varchar2(1000));

Table created.

SQL> insert into t values ('x','x');

1 row created.

SQL> select dump(c1), dump(c2) from t;

DUMP(C1)
------------------------------------------------------------
DUMP(C2)
------------------------------------------------------------
Typ=1 Len=1: 120
Typ=1 Len=1: 120


See how both consume just the 1 byte of data.

You can get show_space here

https://asktom.oracle.com/pls/asktom/f?p=100:11:::::p11_question_id:231414051079

Thank you Connor

A reader, January 17, 2017 - 6:58 am UTC

Glad we could get such valuable support
Connor McDonald
January 18, 2017 - 12:28 am UTC

glad we could help

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library