You are on page 1of 4

Primary and Unique Key Constraints Administration Tips

Primary and Unique Key Constraints

Any time you declare a Primary or Unique Key constraint on a table, Oracle will
automatically create an index for you on the constrained column (unless you already
created your own one).

For example, following this command:

CREATE TABLE CON_TEST (


COL1 NUMBER CONSTRAINT CONTEST_COL1_PK PRIMARY KEY,
COL2 CHAR(5));

...you can immediately issue this query:

SELECT INDEX_NAME, INDEX_TYPE, TABLE_NAME


FROM DBA_INDEXES
WHERE INDEX_NAME LIKE 'CON%';

... and get this sort of report:

INDEX_NAME UNIQUENESS TABLE_NAME


-------------------- ----------------------- ------------
CONTEST_COL1_PK UNIQUE CON_TEST

...and you'll notice immediately two key things. First, the index name is the same as the
constraint name specified during the "create table" statement (and that's an excellent
reason for getting into the habit of naming your constraints properly whenever you define
them). And secondly, Oracle created a unique index for us. Both these things happen
when you declare unique constraints, too, not just primary key ones.

The last fact, that the index was unique, might not be so surprising, if you think about it:
after all, the very definition of a primary key is that every entry is unique. And unique key
constraints will also be unique (!) -so its blindingly obvious that Oracle will create a unique
index. Isn't it?

Well, it might be blindingly obvious, (it also happens not always to be true, but more on
that later) but it has one unfortunate side effect. If I disable my primary key constraint
(perhaps because I am going to do some maintenance work, or a million row insert), I do it
like this:

ALTER TABLE CON_TEST DISABLE CONSTRAINT CONTEST_COL1_PK;

If I then immediately re-submit my query about the indexes associated with this table, I
get this:

Copyright © Howard Rogers 2001 10/18/2001 Page 1 of 4


Primary and Unique Key Constraints Administration Tips

SELECT INDEX_NAME, INDEX_TYPE, TABLE_NAME


FROM DBA_INDEXES
WHERE INDEX_NAME LIKE 'CON%';

NO ROWS SELECTED

In other words, disabling the constraint caused the index to be dropped.

This behaviour is potentially appalling. There you are, intending to disable the constraint
for two minutes' maintenance work, and woosh! An entire 10Gb index disappears -which
means that when I re-enable the constraint, the entire thing has to be rebuilt from scratch,
necessitating exclusive table locks until the entire 10Gb edifice is reconstructed (and this
has actually happened to me!)

So there's the first thing to learn about both primary key and unique key constraints: if
enforced with a unique index, the index is dropped when the constraint is disabled, and
re-built when re-enabled.

Is there anything to be done about this? Well, not in Oracle 7 there isn't. But in Oracle 8.0,
the idea of a 'deferred' constraint was introduced.

A deferred constraint is one that is not checked as each insert is performed, but is checked
when all the inserts have finished, and the User attempts a commit. If at that point, a
record that violates the constraint is detected, the complete set of inserts is rolled back.
One of the main ideas behind deferring constraints in this way is that it allows bulk loads
of data to proceed much faster than if the constraint is "immediate" -there's one mammoth
check at the end of the load, rather than 10,000,000 little immediate checks as each row
of new data is inserted.

But think about it... If we are inserting data which might temporarily violate the
constraint, fair enough that Oracle will sort that out when we come to commit. But in the
meantime, where does that bit of bad data have to reside? Well, in the table, of course
(since data can't just float around in the ether!). So, when the constraint is deferred,
records which violate the constraint must be allowed to get into the table (although they'll
be kicked out when we commit).

But how can a non-unique record reside in the table, however temporarily, if there's a
unique index on that table? The answer is it can't -the index will protest at the inclusion
of non-unique leaf entries, and will throw an error. So how then can we ever defer a
constraint if there is a unique index backing it up, as the example shown above makes
clear there will be?

Well, the answer is that we can't. If the index is unique, the constraint can not be
deferred. But put it the other way around: if the constraint is deferred, the index can not

Copyright © Howard Rogers 2001 10/18/2001 Page 2 of 4


Primary and Unique Key Constraints Administration Tips

be unique! Even more importantly, the determinant is whether the constraint is


deferrable, not whether it is actually deferred or not. A deferrable constraint can be
'initially immediate' or 'initially deferred'. It's the fact that it might one day be deferred
that is important to Oracle.

Therefore, if I issue the following command:

CREATE TABLE CONTEST2 (


COL1 NUMBER CONSTRAINT CONTEST2_COL1_PK
PRIMARY KEY DEFERRABLE INITIALLY IMMEDIATE,
COL2 CHAR(5));

...and then re-run my query for the index details, we see this:

INDEX_NAME UNIQUENES TABLE_NAME


------------------------ --------- ---------------
CONTEST2_COL1_PK NONUNIQUE CONTEST2

...and we discover that a primary key constraint is now being enforced with a NON-unique
index.

So what happens when we disable the constraint now?

SQL> ALTER TABLE CONTEST2 DISABLE CONSTRAINT CONTEST2;

TABLE ALTERED.

SQL> SELECT INDEX_NAME, UNIQUENESS, TABLE_NAME


2 FROM DBA_INDEXES
3 WHERE INDEX_NAME LIKE 'CON%';

INDEX_NAME UNIQUENES TABLE_NAME


------------------------------ --------- --------------
CONTEST2 NONUNIQUE CONTEST2

...and now you'll notice that the index is retained after the constraint is disabled.

Summing all that up, we get a very simple rule: the index associated with a primary or
unique key constraint is dropped when the constraint is disabled if it is unique, but is
retained if it is non-unique.

And that translates into a simple rule of thumb for most DBAs: always make your unique
and primary key constraints 'deferrable'. Whether you choose then to make them actually
deferred or initially immediate is up to you. But by making them deferrable, you put

Copyright © Howard Rogers 2001 10/18/2001 Page 3 of 4


Primary and Unique Key Constraints Administration Tips

yourself in control over when indexes are dropped, not let Oracle silently make the
decision for you.

Before buying into that piece of advice, you might reasonably ask whether the fact that a
constraint is using a unique index makes it faster than if it using a non-unique index. Are
there any performance penalties in making constraints deferrable?

The answer is no. It's just as quick to use a non-unique index as a unique one. That might
surprise you, but the reason for it isn't hard to find. Although the index is created as non-
unique, its contents will actually be unique, because even when deferrable, the constraint
IS enforced at commit time. Any violating records found at that point are rolled back out
of the table (and hence out of the index) -so the index contents are indeed going to be
unique in practice. The optimiser is smart enough to know this, and therefore it treats a
non-unique index which it knows to be enforcing a primary or unique key constraint in
exactly the same way as it would a unique index. In effect, it totally ignores the
'uniqueness' attribute of an index when working out how best to answer a query or perform
some DML.

One final point which many DBAs seem to overlook. The indexes that are created when
unique and primary key constraints are defined need to be housed in a tablespace
somewhere. In none of the examples I've shown above have I mentioned that: and
accordingly, all my indexes would have been created in the default tablespace of the User
who was issuing the 'create table' commands.

That's not usually what you want: indexes are normally sent to their own tablespace. That
needs to be specified when you define the constraint like this:

CREATE TABLE CONTEST2 (


COL1 NUMBER CONSTRAINT CONTEST2_COL1_PK
PRIMARY KEY DEFERRABLE INITIALLY IMMEDIATE
USING INDEX
TABLESPACE INDX01,
COL2 CHAR(5));

The 'using index' clause is the one that does the deed. You can also stick a storage clause
in there if you really want to (my advice is never to specify storage clauses at the segment
level -that's why there's a tablespace 'default storage' clause!).

In summary: primary and unique key constraints should always be declared deferrable,
and should always include a 'using index' clause to house the associated index in an
appropriate tablespace.

Copyright © Howard Rogers 2001 10/18/2001 Page 4 of 4

You might also like