Skip to Main Content
  • Questions
  • How to create constraints and indexes for very large table/Running out of TEMP space

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Jacob.

Asked: August 29, 2017 - 1:11 am UTC

Last updated: August 29, 2017 - 3:23 pm UTC

Version: 11.2.0.4

Viewed 10K+ times! This question is

You Asked

I am rebuilding a database on a new server and I have reloaded the tables via datapump exp/imp. I have one particular table that has a range partition by year and has roughly 20+ billion rows of data. When I try to add the constraints and indexes I was getting errors that I was running out of TEMP space and I had increased it to about 500GB and was still running out of space.

I did figure out how to handle the indexes by creating the index unusable and then rebuilding each partition individually which works but have not figured out how to get the constraints created.

Will I avoid using TEMP space if I create the constraint with the NOVALIDATE option?

No data has been added so I know that there are no dups, I just want it enforced going forward.

and Chris said...

So, let's create a massive table and see what happens:

create table t as 
with rws as (
  select * from dual
  connect by level <= 10000
)
  select rownum c1, sysdate c2, dbms_random.string('a', 20) c3 
  from   rws cross join rws;


You refer to dups, so I'm guessing you're talking about primary/unique constraints. And check constraints etc. are unlikely to need temp anyway:

alter table t add constraint c check ( c1 > 0 ) ;  

select n.name, value from v$mystat ms
join   v$statname n
on     n.STATISTIC# = ms.STATISTIC#
where  lower(n.name) like 'phy%temp%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
physical reads direct temporary tablespace                                0
physical writes direct temporary tablespace                               0


It's a huge table. So we need a lot of temp to add a unique constraint:

alter table t add constraint u unique (c1, c3) ;

select n.name, value from v$mystat ms
join   v$statname n
on     n.STATISTIC# = ms.STATISTIC#
where  lower(n.name) like 'phy%temp%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
physical reads direct temporary tablespace                           591234
physical writes direct temporary tablespace                          591234


How much do we save by making it novalidated instead?

alter table t drop constraint u;
alter table t add constraint u unique (c1, c3) novalidate;

select n.name, value from v$mystat ms
join   v$statname n
on     n.STATISTIC# = ms.STATISTIC#
where  lower(n.name) like 'phy%temp%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
physical reads direct temporary tablespace                          1182468
physical writes direct temporary tablespace                         1182468


WHAT?! Nothing??

Why's that?

Well when you create a unique constraint or primary key, Oracle Database creates a unique index to support it. Even if you don't validate the constraint:

select index_name, uniqueness from user_indexes
where  table_name = 'T';

INDEX_NAME                     UNIQUENES
------------------------------ ---------
U                              UNIQUE


So what happens if we create a non-unique index as part of adding the constraint:

alter table t drop constraint u;
alter table t add constraint u unique (c1, c3) using index (
  create index i on t (c1, c3)
) novalidate ;

select n.name, value from v$mystat ms
join   v$statname n
on     n.STATISTIC# = ms.STATISTIC#
where  lower(n.name) like 'phy%temp%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
physical reads direct temporary tablespace                          1773702
physical writes direct temporary tablespace                         1773702


Hmmm. No better...

Unless the index is already in place!

alter table t drop constraint u keep index;
alter table t add constraint u unique (c1, c3) 
  using index i novalidate ;

select n.name, value from v$mystat ms
join   v$statname n
on     n.STATISTIC# = ms.STATISTIC#
where  lower(n.name) like 'phy%temp%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
physical reads direct temporary tablespace                          1773702
physical writes direct temporary tablespace                         1773702


No temp usage :)

So if you can create the first, then add the constraints using these indexes you can get around this issue.

Note to support unique/primary keys, either you need global indexes or local ones prefixed with the partition key. So your existing "rebuild by partition" strategy may not work...

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

More to Explore

Design

New to good database design? Check out Chris Saxon's full fundamentals class.