The primary key requirement is not standard SQL (even though it ought to be). Eventually omission of a primary key will be allowed, and will result in generation of a hidden system-owned surrogate key.
create table ENLISTMENT(
    NAME varchar(128) not null,
    RANK char(3),
    SERIALNO integer not null constraint SERIALNO_PK primary key,
    PLATOONID integer not null)
create clustered index ENLISTMENT_CX on ENLISTMENT(NAME)
create index ENLISTMENT_PLATOONID on ENLISTMENT(PLATOONID)
Each index has a defined key which is the column list specified by the DDL statement which created the index. For this example:
| NAME | RANK | SERIALNO | PLATOONID | 
| Boyle | CPL | 1004 | 2 | 
| Carter | SGT | 1001 | 2 | 
| Carter | PVT | 1003 | 2 | 
| Lombardi | PVT | 1002 | 1 | 
| Pyle | PVT | 1000 | 1 | 
Note that there is a duplicate in the defined key NAME of the clustered index. This is allowed since the clustered index was not specified as unique. However, we need a unique locator for every tuple stored, so we introduce the concept of an index's distinct key. For a unique index, this is identical to the defined key. For a non-unique clustered index, we append the columns of the primary key (leaving out any that were already referenced by the defined key). In this example, the distinct key for ENLISTMENT_CX is (NAME, SERIALNO).
| SERIALNO | NAME | 
| 1000 | Pyle | 
| 1001 | Carter | 
| 1002 | Lombardi | 
| 1003 | Carter | 
| 1004 | Boyle | 
Note that in this case, adding on the clustered index key does not make the unclustered index key "more unique" since it is already unique; but it does give us an indirect access path from SERIALNO to tuple: first search SERIALNO_PK to convert the SERIALNO into a NAME, and then use the (NAME,SERIALNO) combination to search ENLISTMENT_CX, which stores the values for all other columns.
For non-unique index ENLISTMENT_PLATOONID, adding on the distinct key of the clustered index serves both purposes:
| PLATOONID | NAME | SERIALNO | 
| 1 | Lombardi | 1002 | 
| 1 | Pyle | 1000 | 
| 2 | Boyle | 1004 | 
| 2 | Carter | 1001 | 
| 2 | Carter | 1003 | 
In this case of a non-unique unclustered index, the full tuple stored in the BTree forms the distinct key for the unclustered index. To summarize distinct keys:
select NAME,RANK,SERIALNO
from ENLISTMENT
where NAME='Pyle'
select NAME
from ENLISTMENT
where SERIALNO=1000
So, the coverage for a clustered index is the entire table, while the coverage for an unclustered index is the combination of its defined key with the distinct key of the clustered index. Examples:
| End $Id: //open/dev/farrago/doc/design/TableIndexing.html#4 $ |