Could someone clarify what is the purpose of having unique index without unique constraint (Oracle)?
For example,
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
So far it looks like there is a constraint. But
create table test33(id int not null primary key,
test22_id int not null,
foreign key(test22_id) references test22(id) );
also fails with "ORA-02270: no matching unique or primary key for this column-list".
I'm totally confused by this behaviour. Is there a constraint or not?
There are many articles that explain why it's possible to have a unique constraint without unique index; that is clear and makes perfect sense. However, I don't understand the reason for unique index without constraint.
A constraint and an index are separate logical entities. A unique constraint, for example, is visible in USER_CONSTRAINTS (or ALL_CONSTRAINTS or DBA_CONSTRAINTS). An index is visible in USER_INDEXES (or ALL_INDEXES or DBA_INDEXES).
A unique constraint is enforced by an index though it is possible (and sometimes necessary) to enforce a unique constraint using a non-unique index. A deferrable unique constraint, for example, is enforced using a non-unique index. If you create a non-unique index on a column and subsequently create a unique constraint, you can also use that non-unique index to enforce the unique constraint.
In practice, a unique index acts very much like a unique, non-deferrable constraint in that it raises the same error that a unique constraint raises since the implementation of unique constraints uses the index. But it is not quite the same because there is no constraint. So, as you've seen, there is no unique constraint so you cannot create a foreign key constraint that references the column.
There are cases where you can create a unique index that you cannot create a unique constraint. A function-based index, for example, that enforces conditional uniqueness. If I wanted to create a table that supported logical deletes but ensure that COL1 is unique for all non-deleted rows
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.
But if we're talking about a straight unique non-function based index, there are probably relatively few cases where it really makes more sense to create the index rather than creating the constraint. On the other hand, there are relatively few cases where it makes much difference in practice. You'd almost never want to declare a foreign key constraint that referenced a unique constraint rather than a primary key constraint so you rarely lose something by only creating the index and not creating the constraint.
As was already explained in other answers: constraints and the indexes are different entities. But they lack precise definitions and official comments on the topic. Before we discuss the relationship between these two entities lets take a look at their purpose independent of each other.
Purpose of a constraint1:
Use a constraint to define an integrity constraint-- a rule that restricts the values in a database.
The purposes of an index2:
You can create indexes on columns to speed up queries. Indexes provide faster access to data for operations that return a small portion of a table's rows.
In general, you should create an index on a column in any of the following situations:
The column is queried frequently.
A referential integrity constraint exists on the column.
A UNIQUE key integrity constraint exists on the column.
Now we know what constraints and indexes are, but what is the relationship between them?
The relationship between indexes and constraints is3:
a constraint MIGHT create an index or use an existing index to efficient enforce itself. For example, a PRIMARY KEY constraint will either create an index (unique or non-unique depending) or it will find an existing suitable index and use it.
an index has nothing to do with a constraint. An index is an index.
So, a constraint MIGHT create/use and index. An INDEX is an INDEX, nothing more, nothing less.
So sum this up and directly address the following sentence from your question:
However, I don't understand the reason for unique index without constraint.
Indexes speed up queries and integrity checks (constraints). Also for conditional uniqueness a unique (functional) index is used as this cannot be achieved with a constraint.
Hopefully this brings a little bit more clarification to the whole topic, but there is one aspect of the original question that remains unanswered:
Why did the following error occur when no constraint existed:
ORA-00001: unique constraint (TEST.IDX_TEST22) violated
The answer is simple: there is no constraint and the error message misnames it!
See the official "Oracle Ask TOM" comment 4 on the same problem:
It isn't a constraint. the error message "misnames" it.
If it were a constraint, you could create a foreign key to it -- but you cannot.
Hope it helps.
Links:
1 Oracle 10g Documentation on Constraints
2 Oracle 10g Documentation on Selecting an Index Strategy
3 4 "Oracle Ask TOM" answer to a similar problem
Another point which may be useful in this context is :
Disabling/Dropping an existing unique constraint do not drop the underlying unique index. You got to drop the unique index explicitly.
You can not make conditional uniqueness by declaring a unique constraint, But you can do it by declaring a unique index.
Supporse if you try to execute below:
alter table test22
add constraint test22_u
unique (id, case when tmp = 'aaa' then null else tmp end);
ORA-00904: : invalid identifier
But if you can do it by using the unique index
create unique index test22_u
on test22 ( customer_id,
case when is_default = 'Y' then null else address_id end)
Related
1.In which order should we apply primary key, foreign key constraints and create index if the Oracle table has millions of data and does not have prior constraints?
2.Can we use 'NOLOGGING PARALLEL' while applying primary key and foreign key constraints like we do while applying(creating) indexes? Or any other method so that primary key and foreign key constraints could be applied faster?
Note: I'll use bullets so that it is easier to read, as it is easy to get lost in long sentences.
My thoughts on the subject; see if anything of this helps.
Well,
as you can't create a foreign key constraint if column(s) it references aren't part of primary or unique key
you'll obviously first have to create primary key constraints
and then foreign key constraints
When you
create a primary key constraint,
Oracle automatically creates index that supports it, unless there's already an index you can use (with the USING INDEX clause)
which means that you can "skip" some indexes (those for primary key constraints as they'll already exist) and virtually save some time
and create "other" indexes
On the other hand,
if you first create unique index on future primary key columns and
later add primary key constraint with the USING INDEX clause, Oracle will "skip" check for possible duplicate values because unique index won't allow them
The same goes for
NOT NULL constraint on future primary key columns; primary key doesn't allow NULLs so - if a column already is NOT NULL, enforcing primary key constraint can skip NULL check as well
I don't know
which columns you'll additionally index, but - as you're on Oracle 11g -
don't forget to index all foreign key constraint columns
because you might encounter unexpected table locks if you
update primary key column in parent table, or
delete parent record
Can you do it with no logging and in parallel? Yes:
SQL> create table test (id number, name varchar2(20));
Table created.
SQL> create unique index ui1_test_id on test (id) nologging parallel 20;
Index created.
SQL> alter table test add constraint pk_test primary key (id) using index ui1_test_id nologging parallel 20;
Table altered.
SQL>
But you'll probably want to change the index and table back to NOPARALLEL and LOGGING when the initial creation is done.
SQL> alter index ui1_test_id noparallel;
Index altered.
SQL> alter table test logging noparallel;
Table altered.
I'm trying to make sure I'm getting the benefit of selecting from a partition when using reference partitions.
In normal partitions, I know you have to include the column(s) on which the partition is defined in order for Oracle to know it can just search one specific partition.
My question is, when I'm selecting from a reference-partitioned table, do I just need to include the column on which the reference foreign key is defined? Or do I need to join and include the parent table's column on which the partition is actually defined?
create table alpha (
name varchar2(240) not null,
partition_no number(14) not null,
constraint alpha_pk
primary key (name),
constraint alpha_c01
check (partition_no > 0)
)
partition by range(partition_no)
interval (1)
(partition empty values less than (1))
;
create table beta (
name varchar2(240) not null,
alpha_name varchar2(240) not null,
some_data number not null,
constraint beta_pk
primary key (name),
constraint beta_f01
foreign key (alpha_name)
references alpha (name)
)
partition by reference (beta_f01)
;
Assume the tables in production will have much more data in them, with hundreds of millions of rows in the beta table, but merely thousands per partition.
Is this all I need?
select b.some_data
from beta b
where b.alpha_name = 'Blah'
;
Thanks if anyone can verify this for me. Or can explain anything else I'm missing with regard to properly creating indexes in reference-partitioned tables.
[Edit] Removed part of the example where clause that shouldn't have been there. The example is meant to represent reading the reference-partitioned with just the reference partition foreign key in the where clause.
So a table I am looking at has a unique constraint AND a unique index over multiple columns, and the exact same columns for both.
Is there a use for this or is the unique constraint redundant?
I agree that the existence of unique constraints and unique indexes does look redundant at first. It seems like a violation of Don't Repeat Yourself, allowing for confusing differences. But there are at least two reasons both exist - management features and allowing existing duplicates.
Management Features
In theory, a logical constraint can be created without worrying about the implementation. The constraint specifies what must be true, along with some options such as deferring the constraint until a commit.
In practice, constraints have such a large performance and storage penalty that the implementation must be considered. An index is required or else a single insert would require O(n) time instead of O(log(n)). Those indexes can take up a huge amount of space; someone might want to specify where it's stored, how it's compressed, etc.
Most of the time those features aren't important and using all the index defaults is fine. But sometimes storage and performance are critical and people will want to tweak the index without caring about the constraint.
Allow Existing Duplicates
There is at least one case where a unique constraint does not have a unique index. It's possible to allow existing duplicate valuess but prevent any future duplicates by setting the constraint to NOVALIDATE and using a non-unique index.
--Create table and insert duplicate values.
create table test1(a number);
insert into test1 values(1);
insert into test1 values(1);
commit;
--Add a non-validated unique constraint, with a non-unique index.
alter table test1
add constraint test1_uq unique(a)
using index (create /* Not unique!*/ index test1_uq on test1(a)) novalidate;
--Now multiple inserts raise: ORA-00001: unique constraint (JHELLER.TEST1_UQ) violated
insert into test1 values(2);
insert into test1 values(2);
The physical index must allow duplicates, but the logical constraint knows to not allow any more duplicates. Although this is a rare feature and I'm not sure if I've ever seen it in production code.
I need to modify an existing PK. Therefore I drop an recreate it.
ALTER TABLE B DROP CONSTRAINT PK_B;
ALTER TABLE B ADD CONSTRAINT PK_B PRIMARY KEY ("TYP", "NR", "HH", "QUART");
Unfortunately the last Statement will give me an error ORA-00955
If I create the PK constraint like it was defined originally with:
ALTER TABLE B ADD CONSTRAINT PK_B PRIMARY KEY ("TYP", "NR", "HH");
everything works fine.
Perhaps there is an INDEX associated with the PRIMARY KEY CONSTRAINT, and it is also named as PK_B.
You can check it as :
SELECT * FROM USER_INDEXES WHERE TABLE_NAME='<table_name>';
If that's true, then do :
ALTER INDEX "PK_B" RENAME TO "PK_XYZ";
Update : Regarding ALTER INDEX statement, few important points as mentioned by Justin in the comments
Oracle implicitly creates an UNIQUE index to support the PRIMARY KEY CONSTRAINT. Since, the index is of the same name that of the primary key, and now that the primary key is being modified, it is better to drop and re-create the index again as per the definition of the old primary key.
My conclusion :
The primary key constraint is enforced through a unique index.
If Oracle already finds an index – unique or non-unique – it uses it
for the primary key.
If the index was initially created as non-unique, it will continue to
show as non-unique, however it will actually be a unique index.
A good demonstration and quite detailed on other aspects too, by Arup : Primary Keys Guarantee Uniqueness? Think Again.
I had the same issue where I had to do the following to delete reference to a table from the view whilst recreating the database from the scratch. I was searching for the same in tables and indexes first.
connect sys/oracle as sysdba;
select * from all_tables
select * from all_indexes
(finally located the reference in the views)
select * from all_views where view_name like '%WKSTSTATE%';
drop view RUEGEN.WKSTSTATE;
I have some problems with Oracle Foreign Composite keys. I have an application that is somewhat big (you know, 5000+ tables, that kind of thing) and we store some 'static' (it actually can change) data into some set of tables. This data is referenced by a lot of the tables through the database, so it worked like this:
TABLE StaticData
ID(PK) Data
1 StaticData1
2 StaticData2
...
n StaticDataN
TABLE TypicalTable
ID(PK) StaticDataID(FK to StaticData)
1 1
2 1
3 7
4 2
...
n n
And all was well in Wonderland.
But some changes of spec, and some meetings with the client after, we were tasked with having different 'versions' of the data ready to replace the static data when some time arrives. Last part was easy, we can create jobs that will check every day/week for a date and change the data, but we will have to maintain older and newer versions of the data... in the same table. So now StaticData looks like:
TABLE StaticData
ID(PK) Data KickInDate(Also PK)
1 StaticData1.1 01/01/1900
1 StaticData1.2 10/07/2014
1 StaticData1.3 12/12/2015
2 StaticData2.1 01/01/1900
...
n StaticDataN.1 01/01/1900
And of course all integrity reference has gone off the board. And of course, since I cannot put a UNIQUE constraint in the ID, I cannot keep the foreign keys.
I have searched the net for a solution for this (less restrictive kind of foreign keys) and most of the time the solution is to use triggers checking BEFORE INSERT|UPDATE|DELETE
But that will be kind of a very, very, very big job.
So I ask, Do I have other solutions?
Is there any way to tell Oracle to reference another column of another table even thought is not UNIQUE? (it will definitely be NOT NULL).
Primary keys in Oracle can have duplicates. Primary keys can be built with non-unique indexes and existing values can be excluded by creating the constraint with NOVALIDATE. It is a rarely used feature, will confuse people, and is not a clean solution. But in the real world sometimes data isn't clean and there's no time for the perfect solution.
Sample schema and data.
create table staticData
(
id number not null,
data varchar2(100),
constraint staticData_pk primary key (id)
);
create table typicalTable
(
id number not null,
staticDataID number,
constraint typicalTable_pk primary key (id),
constraint typicalTable_fk foreign key (staticDataID)
references staticData(id)
);
insert into staticData values (1, 'StaticData1');
insert into staticData values (2, 'StaticData2');
insert into typicalTable values(1, 1);
insert into typicalTable values(2, 1);
Process to drop constraints, add duplicate data, and re-enable constraints.
--Drop constraints.
alter table typicalTable drop constraint typicalTable_fk;
alter table staticData drop constraint staticData_pk;
--Add semi-duplicate data.
insert into staticData values (1, 'StaticData1.2');
--Use a non-unique index to build a NOVALIDATE primary key.
create index staticData_pk on staticData(id);
alter table staticData add constraint staticData_pk primary key (id) novalidate;
alter table typicalTable add constraint typicalTable_fk foreign key(staticDataID)
references staticData(id);
No, the target column(s) must be unique, that's the whole idea. However, you can propagate an additional version column from StaticData to TypicalTable:
CREATE TABLE StaticData (
id NUMBER,
version NUMBER,
col1 ... coln,
PRIMARY KEY (id,version)
);
CREATE TABLE TypicalTable (
StaticDataID NUMBER,
version NUMBER,
colx ... coly,
FOREIGN KEY (StaticDataID, version) REFERENCES StaticData(id, version)
);