Skip to Main Content
  • Questions
  • Implicit indexes for primary/unique key constraints

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, John.

Asked: November 08, 2012 - 9:54 am UTC

Last updated: January 02, 2013 - 12:35 pm UTC

Version: 10.2.0.5

Viewed 10K+ times! This question is

You Asked

Tom - I figure this will be an easy one for you. Its not really a question, but rather a lesson learned. I encountered a situation that I thought was a discrepancy, but it turned out to be a documented behavior. I'm posting this in case anyone else encounters a similar circumstance.

This is regarding the implicit creation of indexes to support unique key constraints. I learned to be explicit in the definition of the indexes if the application is dependent on them. Relying on implicit index creation for these constraints doesn't necessarily produce the desired outcome.

I have a table with a composite primary key of two columns.
create table mytest (
        c1 integer,
        c2 integer,
        constraint mytest_pk primary key (c1, c2),
        constraint mytest_uk unique (c2, c1)
);


I need to query this table in the following manner:

1) select * from mytest where c1 = :x and c2 between :y and :z;
2) select * from mytest where c2 = :x and c1 between :y and :z;

So, I defined both the primary key and unique key constraints to index c1 and c2 both ways - or so I thought.

I used the following view in this test scenario to peek at the indexes on
table mytest and their structure:

column index_name format a20
column column_name format a20
break on index_name skip 1
create or replace view mytest_indexes as
      select substr(idx.index_name,1,20) index_name,
             substr(col.column_name,1,20) column_name,
             idx.status
      from user_ind_columns col,
           user_indexes idx
      where idx.table_name='MYTEST'
      and   col.table_name=idx.table_name
      and   col.index_name = idx.index_name
      order by idx.index_name,
               col.column_position;

select * from mytest_indexes;

INDEX_NAME           COLUMN_NAME          STATUS
-------------------- -------------------- --------
MYTEST_PK            C1                   VALID
                     C2                   VALID

--
-- I expected to see two indexes as a result:
--    mytest_pk and mytest_uk.
--
-- But, NO. Only mytest_pk exists.
--
-- So I tried changing their definition order.
--
drop table mytest;

create table mytest (
        c1 integer,
        c2 integer,
        -- reversed order of PK and UK definition
        constraint mytest_uk unique (c2, c1),
        constraint mytest_pk primary key (c1, c2)
);

select * from mytest_indexes;

INDEX_NAME           COLUMN_NAME          STATUS
-------------------- -------------------- --------
MYTEST_PK            C1                   VALID
                     C2                   VALID

--
-- No difference. Still no index on the unique constraint.
--
-- So I tried forcing the order with separate alter table statements.
--
drop table mytest;

create table mytest (
        c1 integer,
        c2 integer
);

alter table mytest add (constraint mytest_uk unique (c2, c1));
alter table mytest add (constraint mytest_pk primary key (c1, c2));

select * from mytest_indexes;

INDEX_NAME           COLUMN_NAME          STATUS
-------------------- -------------------- --------
MYTEST_UK            C2                   VALID
                     C1                   VALID

--
-- Now an index exists for UK but not for PK.
--
-- So, I tried a brute-force approach.
--
drop table mytest;

create table mytest (
        c1 integer,
        c2 integer
);


alter table mytest add (constraint mytest_uk unique (c2, c1) using index);
alter table mytest add (constraint mytest_pk primary key (c1, c2) using index);

select * from mytest_indexes;

INDEX_NAME           COLUMN_NAME          STATUS
-------------------- -------------------- --------
MYTEST_UK            C2                   VALID
                     C1                   VALID

--
-- Still no good.
--
-- So, I tried explicit 'create index' commands.
--
drop table mytest;

create table mytest (
        c1 integer,
        c2 integer
);

alter table mytest add (
        constraint mytest_uk unique (c2, c1)
            using index (create unique index mytest_uk on mytest(c2, c1))
);

alter table mytest add (
        constraint mytest_pk primary key (c1, c2)
            using index (create unique index mytest_pk on mytest(c1, c2))
);


select * from mytest_indexes;

INDEX_NAME           COLUMN_NAME          STATUS
-------------------- -------------------- --------
MYTEST_PK            C1                   VALID
                     C2                   VALID

MYTEST_UK            C2                   VALID
                     C1                   VALID

--
-- Voila- Success!
--
-- Now, combining all of this into one DDL statement..
--
drop table mytest;

create table mytest (
        c1 integer,
        c2 integer,
        constraint mytest_uk unique (c2, c1)
           using index (create unique index mytest_uk on mytest(c2, c1)),
        constraint mytest_pk primary key (c1, c2)
           using index (create unique index mytest_pk on mytest(c1, c2))
);

--
-- LESSON LEARNED - Don't rely on implicit index creation by a 'create table'
--     command if those indexes are necessary for selectivity.
--     Be explicit!
--
drop table mytest;
drop view mytest_indexes;


and Tom said...

You do not use constraints to create indexes.

you use create index to create indexes.



If you have a need for a SPECIFIC index to exist, you must create it (if it already does, it will fail of course but that is OK, you have what you need)...


primary keys/unique constraints do not even need a unique index - they can use non-unique indexes, they can use indexes that contain more columns then they have themselves as well.

Rating

  (1 rating)

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

Comments

Limitations of Unique Constraint

Bala, December 21, 2012 - 3:00 am UTC

Hi Tom,

Is there any limitations in terms of unique key constraint for a table?

How many unique key constraint can we have in a single table?
Tom Kyte
January 02, 2013 - 12:35 pm UTC

you can have as many constraints as you like.

I'm not sure what you mean by "limitation" in this context. There are limits on the number of columns (pure reason dictates there would be a few columns at most in any given unique constraint).

There are logical limits - if you find every column in your table is unique - you've probably done something wrong. I cannot imagine a case where there'd be more then 3, 4 or maybe 5 unique constraints on a table (doesn't mean they don't exist, they just aren't "normal")

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