web-dev-qa-db-fra.com

Contrainte unique Oracle et index unique

Quelqu'un pourrait-il préciser l'objectif de l'index unique sans contrainte unique (Oracle)? Par exemple,

create table test22(id int, id1 int, tmp varchar(20));
create unique index idx_test22 on test22(id);
insert into test22(id, id1, tmp) values (1, 2, 'aaa'); // ok
insert into test22(id, id1, tmp) values (1, 2, 'aaa'); // fails, ORA-00001: unique   
  // constraint (TEST.IDX_TEST22) violated

Jusqu'à présent, il semble y avoir une contrainte. Mais 

create table test33(id int not null primary key, 
test22_id int not null, 
foreign key(test22_id) references test22(id) );

échoue également avec "ORA-02270: no matching unique or primary key for this column-list". Ce comportement me laisse totalement perplexe. Y a-t-il une contrainte ou pas?

De nombreux articles expliquent pourquoi il est possible d'avoir une contrainte unique sans index unique; cela est clair et parfaitement logique. Cependant, je ne comprends pas la raison de l'index unique sans contrainte.

37
a1ex07

Une contrainte et un index sont des entités logiques distinctes. Une contrainte unique, par exemple, est visible dans USER_CONSTRAINTS (ou ALL_CONSTRAINTS ou DBA_CONSTRAINTS). Un index est visible dans USER_INDEXES (ou ALL_INDEXES ou DBA_INDEXES).

Une contrainte unique est appliquée par un index, bien qu'il soit possible (et parfois nécessaire) d'appliquer une contrainte unique à l'aide d'un index non unique. Une contrainte unique pouvant être différée, par exemple, est appliquée à l'aide d'un index non unique. Si vous créez un index non unique sur une colonne puis créez une contrainte unique, vous pouvez également utiliser cet index non unique pour appliquer la contrainte unique. 

En pratique, un index unique agit pratiquement comme une contrainte unique, non différable, en ce sens qu'il soulève la même erreur que celle provoquée par une contrainte unique, car la mise en œuvre de contraintes uniques utilise l'index. Mais ce n'est pas tout à fait pareil car il n'y a pas de contrainte. Ainsi, comme vous l'avez vu, il n'y a pas de contrainte unique et vous ne pouvez donc pas créer de contrainte de clé étrangère faisant référence à la colonne. 

Dans certains cas, vous pouvez créer un index unique, mais pas une contrainte unique. Un index basé sur une fonction, par exemple, qui applique l'unicité conditionnelle. Si je voulais créer une table qui prenne en charge les suppressions logiques tout en m'assurant que COL1 est unique pour toutes les lignes non supprimées 

SQL> ed
Wrote file afiedt.buf

  1  CREATE TABLE t (
  2    col1 number,
  3    deleted_flag varchar2(1) check( deleted_flag in ('Y','N') )
  4* )
SQL> /

Table created.

SQL> create unique index idx_non_deleted
  2      on t( case when deleted_flag = 'N' then col1 else null end);

Index created.

SQL> insert into t values( 1, 'N' );

1 row created.

SQL> insert into t values( 1, 'N' );
insert into t values( 1, 'N' )
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.IDX_NON_DELETED) violated


SQL> insert into t values( 1, 'Y' );

1 row created.

SQL> insert into t values( 1, 'Y' );

1 row created.

Mais si nous parlons d'un index unique et non basé sur une fonction directe, il y a probablement relativement peu de cas où il est plus logique de créer un index plutôt que de créer une contrainte. Par contre, il y a relativement peu de cas où cela fait beaucoup de différence dans la pratique. Vous ne voudriez presque jamais déclarer une contrainte de clé étrangère qui faisait référence à une contrainte unique plutôt qu'à une contrainte de clé primaire; vous perdez donc rarement quelque chose en créant uniquement l'index et en ne créant pas la contrainte.

49
Justin Cave

Un autre point qui peut être utile dans ce contexte est le suivant: Vous devez supprimer explicitement l'index unique.

2
Syamjith

Vous ne pouvez pas créer une unicité conditionnelle en déclarant une contrainte unique, mais vous pouvez le faire en déclarant un index unique.

Supposez si vous essayez d'exécuter ci-dessous: 

alter table test22 
add constraint test22_u 
unique (id, case when tmp = 'aaa' then null else tmp end);

ORA-00904: : invalid identifier 

Mais si vous pouvez le faire en utilisant l'index unique 

create unique index test22_u 
on test22 ( customer_id, 
case when is_default = 'Y' then null else address_id end)
0
vidyut07