Create unique constraint with nvl in Oracle - oracle

How can I create unique constraint when I need to treat null values as equals.
For
alter table T1 add constraint T1_UN
unique (nvl(C1, ' '))
i get
ORA-00904: : invalid identifier
points on nvl
Thanks!

NOT NULL seems like a better idea, but you could make a function-based index unique:
create unique index idx_t1 on t1 (nvl(C1, ' '));

Just make the column NOT NULL.
The nature of NULL is that it is never equal to anything. Hence every NULL value in your column is already UNIQUE in a way. It doesn't make sense to include them in a UNIQUE key like you want to. The reason why you want to do it is probably an existing data integrity problem. So to make the column NOT NULL, you might have to migrate NULL values to something else, first...
Note: You can make the column just simply UNIQUE. Then you can have several NULL values as described above...

Related

Adding NOT NULL Constraint with ALTER TABLE statement (Oracle)

this is my PL-SQL statement
ALTER TABLE regions MODIFY (region_name VARCHAR(40) DEFAULT 'Euro') CONSTRAINT region_nn NOT NULL;
The column 'region_name' has NULL values I want to replace with 'Euro'. I get an error with this, and I'm wondering if I have the syntax wrong or if it's impossible to place a default value when adding the NOT NULL constraint and I have to do it as two separate SQL statements
Thank you for your help'
adding a constrain does not modify any existing data, it only modifies the definition of your table. Fix your data first, then add the constraint - or add the constraint with the defererred keyword and then fix the data. Either way, you'll manually have to update the data.

Unique constraint violation during insert: why? (Oracle)

I'm trying to create a new row in a table. There are two constraints on the table -- one is on the key field (DB_ID), the other constrains a value to be one of several the the field ENV. When I do an insert, I do not include the key field as one of the fields I'm trying to insert, yet I'm getting this error:
unique constraint (N390.PK_DB_ID) violated
Here's the SQL that causes the error:
insert into cmdb_db
(narrative_name, db_name, db_type, schema, node, env, server_id, state, path)
values
('Test Database', 'DB', 'TYPE', 'SCH', '', 'SB01', 381, 'TEST', '')
The only thing I've been able to turn up is the possibility that Oracle might be trying to assign an already in-use DB_ID if rows were inserted manually. The data in this database was somehow restored/moved from a production database, but I don't have the details as to how that was done.
Any thoughts?
Presumably, since you're not providing a value for the DB_ID column, that value is being populated by a row-level before insert trigger defined on the table. That trigger, presumably, is selecting the value from a sequence.
Since the data was moved (presumably recently) from the production database, my wager would be that when the data was copied, the sequence was not modified as well. I would guess that the sequence is generating values that are much lower than the largest DB_ID that is currently in the table leading to the error.
You could confirm this suspicion by looking at the trigger to determine which sequence is being used and doing a
SELECT <<sequence name>>.nextval
FROM dual
and comparing that to
SELECT MAX(db_id)
FROM cmdb_db
If, as I suspect, the sequence is generating values that already exist in the database, you could increment the sequence until it was generating unused values or you could alter it to set the INCREMENT to something very large, get the nextval once, and set the INCREMENT back to 1.
Your error looks like you are duplicating an already existing Primary Key in your DB. You should modify your sql code to implement its own primary key by using something like the IDENTITY keyword.
CREATE TABLE [DB] (
[DBId] bigint NOT NULL IDENTITY,
...
CONSTRAINT [DB_PK] PRIMARY KEY ([DB] ASC),
);
It looks like you are not providing a value for the primary key field DB_ID. If that is a primary key, you must provide a unique value for that column. The only way not to provide it would be to create a database trigger that, on insert, would provide a value, most likely derived from a sequence.
If this is a restoration from another database and there is a sequence on this new instance, it might be trying to reuse a value. If the old data had unique keys from 1 - 1000 and your current sequence is at 500, it would be generating values that already exist. If a sequence does exist for this table and it is trying to use it, you would need to reconcile the values in your table with the current value of the sequence.
You can use SEQUENCE_NAME.CURRVAL to see the current value of the sequence (if it exists of course)

Oracle unique constraint and unique index

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)

Using JPA/Oracle can I have a unique constraint that ignores string case?

I have a db table with a column that is a String. I do not consider the case to be significant (e.g. "TEST == "test"). Unfortunately, it appears that JPA2 does, because both values are inserted into my table; I would like the second one to be rejected.
Is there a generic way to annotate an "ignore-case" unique constraint on a string column?
As an alternative, I could also consider putting a unique "ignore-case" constraint on the actual db column. Is that possible in Oracle 10?
What I don't want to do is write code, because this occurs often in this particular db.
All help is greatly appreciated.
you can achieve this with a function-based unique index
create unique index <index_name> on <table_name> (UPPER(<column_name>));
for Example
create table t111( col varchar2(10));
create unique index test_idx on t111 (UPPER(col));
insert into t111 values('test');
insert into t111 values ('TEST');

How to constrain a database table so only one row can have a particular value in a column?

Using Oracle, if a column value can be 'YES' or 'NO' is it possible to constrain a table so that only one row can have a 'YES' value?
I would rather redesign the table structure but this is not possible.
[UDPATE] Sadly, null values are not allowed in this table.
Use a function-based index:
create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);
Oracle only indexes keys that are not completely null, and the CASE expression here ensures that all the 'NO' values are changed to nulls and so not indexed.
This is a kludgy hack, but if the column allows NULLs, then you could use NULL in place of "NO" and use "YES" just as before. Apply a unique key constraint to that column, and you'll never get two "YES" values, but still have many NOs.
Update: #Nick Pierpoint: suggested adding a check constraint so that the column values are restricted to just "YES" and NULL. The syntax is all worked out in his answer.
You will want to check a Tom Kyte article with exactly this question being asked and his answer:
http://tkyte.blogspot.com/2008/05/another-of-day.html
Summary: don't use triggers, don't use autonomous transactions, use two tables.
If you use an Oracle database, then you MUST get to know AskTom and get his books.
It doesn't work on the table definition.
However, if you update the table using a trigger calling a stored procedure, you could make sure that only one row contains "YES".
Set all rows to "NO"
Set the row you want to YES
Following on from my comment to a previous answer by yukondude, I'd add a unique index and a check constraint:
create table mytest (
yesorno varchar2(3 char)
);
create unique index uk_mytest_yesorno on mytest(yesorno);
alter table mytest add constraint ck_mytest_yesorno check (yesorno is null or yesorno = 'YES');
Does Oracle support something like filtered indices (last week I heard that e.g. MSSQL2008 does)? Maybe you can define a unique key which applies only to rows with the value "Yes" in your column.
I guess I'd use a second table to point to the appropriate row in your current table. That other table could be used to store values of other variables too too.

Resources