The Constraint Optimization series:
- Part 1: Optimization of Check Constraint Creation
- Part 2: Optimization that Violates Data Integrity
- Part 3: Optimization of Foreign Key Constraint Creation
- Part 4: (Lack of) Optimization of Unique Constraint Creation
- Part 5: Adding a Column with a Default Value and a Constraint
- Part 6: Fast but Offline, or Online but Slow?
- Part 7: Adding a Unique Constraint in an Online Way
- Part 8: Constraint Optimization Summary
I have a table T with many records and I want to add a new column C to this table with some check constraint on C.
Does it matter if I use the following statement
ALTER TABLE T ADD (C NUMBER, CONSTRAINT C_CHK CHECK (C>0));
or this one
ALTER TABLE T ADD (C NUMBER CONSTRAINT C_CHK CHECK (C>0));
?
Note that the only difference is the comma that appears in the first option and not in the second one.
It certainly matters.
In the first option the constraint is an out-of-line (“table-level”) check constraint, while in the second option it is an inline (“column-level”) one.
With out-of-line check constraint Oracle scans the table, and validates that each and every record satisfies the constraint. This is similar to adding the column in one statement, and then adding the constraint in a second statement.
But when we add the column with an inline check constraint in a single statement, Oracle optimizes this process. Since the added column is NULL for all the existing records, it is actually unnecessary to access the records in order to perform the validation.
We can see a small example (tested in 11.2.0.4, 12.1.0.2, 12.2.0.1). First, adding the column with the out-of-line check constraint:
> create table t (x varchar2(1000)); Table created. > insert /*+ append */ into t > select lpad(rownum,1000,'x') from dual connect by level<=1e6; 1000000 rows created. > commit; Commit complete. > col value new_value value_0 noprint > select b.value from v$statname a, v$mystat b > where a.statistic# = b.statistic# and a.name = 'session logical reads'; > set timing on > ALTER TABLE T ADD (C NUMBER, CONSTRAINT C_CHK CHECK (C>0)); Table altered. Elapsed: 00:00:04.27 > set timing off > select b.value-&VALUE_0 logical_reads from v$statname a, v$mystat b > where a.statistic# = b.statistic# and a.name = 'session logical reads'; LOGICAL_READS ------------- 143243
Note the elapsed time and the number of logical reads – most of them are due to accessing all the rows of T in order to validate the check constraint.
Now, let’s see the same example but with the inline check constraint instead of the out-of-line one:
> drop table t purge; Table dropped. > create table t (x varchar2(1000)); Table created. > insert /*+ append */ into t > select lpad(rownum,1000,'x') from dual connect by level<=1e6; 1000000 rows created. > commit; Commit complete. > col value new_value value_0 noprint > select b.value from v$statname a, v$mystat b > where a.statistic# = b.statistic# and a.name = 'session logical reads'; > set timing on > ALTER TABLE T ADD (C NUMBER CONSTRAINT C_CHK CHECK (C>0)); Table altered. Elapsed: 00:00:00.01 > set timing off > select b.value-&VALUE_0 logical_reads from v$statname a, v$mystat b > where a.statistic# = b.statistic# and a.name = 'session logical reads'; LOGICAL_READS ------------- 359
Much faster, with significantly less logical reads, since accessing the actual records is omitted.