Conditional unique constraint in oracle db - oracle

I have a situation where I need to enforce a unique constraint on a column[attribute] depending on another column value.
So for example, I have a table like Table(ID, EID, Name, ISDeleted)
ISDeleted can only have a value null or 'y' (active or deleted), and i want to create a unique constraint on EID, ISDeleted only when ISDeleted = null, since I dont care if there are multiple deleted records with the same id. Please note that, EID can have null value.
I am using Oracle DB for this.

You can't create a constraint. But you can create a unique function-based index. This takes advantage of the fact that Oracle does not index NULL values-- any rows where isDeleted is NOT NULL will not be included in the index so the unique constraint won't apply to them.
CREATE UNIQUE INDEX one_not_deleted
ON table_name( (CASE WHEN isDeleted IS NULL
THEN eid
ELSE null
END) );

Related

Oracle - Unique constraint while allowing null values

I'm a bit new to PL-SQL coming from T-SQL.
I have a requirement that only one phone number is allowed per user ID, but the phone number column can be null as many times as required.
So table is:
User ID
Phone Number
1
NULL
1
9735152122
1
NULL
2
NULL
3
NULL
1
2124821212
It's that last one I need to block, although the first three are fine. In this case I'm talking about the sample table I've posted, not the actual table order. I just need to allow the NULLs through but block if there are duplicate phone numbers per a given User ID.
I've read about functional indexes but not sure exactly how to apply them here.
CREATE UNIQUE INDEX my_index ON my_table (
CASE WHEN phone_number IS NULL THEN NULL ELSE user_id END,
phone_number
)
With this logic, if phone_number is NULL, then both values in the index will be NULL, so that row will be excluded from the index. If phone_number is not NULL, then the row will be included in the index with the actual values for user_id and phone_number, and uniqueness will be enforced.
P.S. This is not "PL/SQL", it is Oracle SQL. PL/SQL is the procedural language used to write such things as triggers, functions, etc.

Oracle - only allow items to be inserted if three column values are unique and a fourth column is not equal to a given char

I am trying to enforce some uniqueness on data at the db level. For the sake of argument, my columns are:
ID
DocID (FK)
FileName
FileRevision
Province
ActiveState
So in this example I want the combo of DocID, FIleName, and FileRevision entered only once. This is no issue, I can just create a UNIQUE constraint.
However the problem is, if 'ActiveState' is set to 'X', I want to allow any combo of those items.
In other words, only force uniqueness if ActiveState <> 'X'.
My understanding is I can not have a UNIQUE constraint with conditions. I've read about indexes, wondering if that's the path I need to travel?
I'm no Oracle expert (obviously), just looking for a tip!
You can use virtual columns and then create a unique constraint on the virtual columns:
CREATE TABLE table_name (
ID NUMBER PRIMARY KEY,
DocID NUMBER,
FileName VARCHAR2(10),
FileRevision NUMBER,
Province NUMBER,
ActiveState VARCHAR2(1),
ActiveDocID GENERATED ALWAYS AS (CASE ActiveState WHEN 'X' THEN NULL ELSE DocID END),
ActiveFileName
GENERATED ALWAYS AS (CASE ActiveState WHEN 'X' THEN NULL ELSE FileName END),
ActiveFileRevision
GENERATED ALWAYS AS (CASE ActiveState WHEN 'X' THEN NULL ELSE FileRevision END),
CONSTRAINT table_name__uniq UNIQUE (
ActiveDocID,
ActiveFileName,
ActiveFileRevision
)
);
For the same, Oracle provides the Conditoinal Index concept. You can create an conditional index on your table like below -
CREATE UNIQUE INDEX idx_ActiveState
ON YOUR_TABLE (CASE WHEN ActiveState = 'X' THEN NULL ELSE DocID END,
CASE WHEN ActiveState = 'X' THEN NULL ELSE FIleName END,
CASE WHEN ActiveState = 'X' THEN NULL ELSE FileRevision END);

MODIFY or ADD to add NOT NULL constraint to a column? Oracle sql

ORDERS table in the Oracle Database:
ORDERS
ORDER_ID NOT NULL NUMBER(4)
ORDATE_DATE DATE
CUSTOMER_ID NUMBER(3)
ORDER_TOTAL NUMBER(7,2)
The ORDERS table contains data and all orders have been assigned a customer ID. I'm trying to add a NOT NULL constraint to the CUSTOMER_ID column. Would I use MODIFY CONSTRAINT or ADD CONSTRAINT? I was told you have to drop the constraint and ADD the new one, but if there is no existing constraint to Customer ID number, would it be MODIFY?
alter table orders modify customer_id not null;
Just MODIFY the column:
alter table orders modify customer_id not null;
Alternatively, you could add an [overkill] constraint in the form:
alter table orders add constraint nn1 check (customer_id is not null);
Just use the first form.
As a side note, some databases (such as Oracle) consider those two constraint different and somewhat separate: the former is a column constraint, while the latter is a table constraint. Oracle keeps track in case you drop one, while the other is still in effect.

Uniqueness constraint on multiple columns causes issue with null values

According to oracle docs, a null cannot be equal or unequal to any value or to another null
This is evident in case of a uniqueness constraint on any column. But the behaviour is different if the uniqueness constraint in on multiple columns. eg:
CREATE TABLE table1 (
col1 NUMBER(2),
col2 NUMBER(2),
CONSTRAINT uniq_col1_col2 UNIQUE (col1, col2)
);
INSERT INTO table1 VALUES (1, NULL);
INSERT INTO table1 VALUES (1, NULL);
# ORA-00001: unique constraint (XYZ.UNIQ_COL1_COL2) violated
Why is this so? And how can I specify the constraint to ignore nulls?
EDIT
More specifically, if rows (null), (null) are unique, why are (1,null), (1,null) not unique? What is the rationale behind this?
That's what the documentation says (emphasis added):
To satisfy a unique constraint, no two rows in the table can have the same value for the unique key. However, the unique key made up of a single column can contain nulls. To satisfy a composite unique key, no two rows in the table or view can have the same combination of values in the key columns. Any row that contains nulls in all key columns automatically satisfies the constraint. However, two rows that contain nulls for one or more key columns and the same combination of values for the other key columns violate the constraint.
It's doing what it's supposed to do. With your two sample inserts, both (potential) rows contain null in one key column and the same value (1) in the other key column, so the constraint is violated.
Nothing else would really make sense; allowing both inserts to go ahead would leave you with two indistinguishable rows, in key terms anyway.
You asked:
More specifically, if rows (null), (null) are unique, why are (1,null), (1,null) not unique? What is the rationale behind this?
Because there is no other key column to enforce uniqueness.
As you said, null isn't equal to or not equal to anything. If your unique key was only on col1 and you had two rows with it set to null, which is allowed, then querying where col1 is null would find both - which is OK because is null isn't about equality. You can say that both rows matched the condition, but not that they are equal to null. With your two-column key the equivalent would be where col1 = 1 and col2 is null. Now equality does come into play.
In both cases the nulls are ignored, and whatever is left still has to be unique. With a single-column key there is nothing else to enforce uniqueness. With the two-column key if col2 is null then col1 is still there to be compared, and that on its own has to be unique, effectively.
You're also allowed to do:
INSERT INTO table1 VALUES (null, null);
INSERT INTO table1 VALUES (null, null);
The same thing applies; they nulls are effectively ignored, but now - as with the single-column-key - there is nothing left to enforce uniqueness against.
If you really want a constraint that ignores nulls and only prevents insert of complete duplicate keys then you can use a function-based unique index that only holds keys that are totally non-null:
create unique index uniq_col1_col2 on table1
( case when col1 is not null and col2 is not null then col1 end
, case when col1 is not null and col2 is not null then col2 end
);

Oracle 12C: Creating Table so that most recent records inserted are on top by default

I am new to Oracle and for the sake of learning, I need to know how to create a table so that the newest records inserted are at the top.
In TSQL: I will do a CLUSTERED INDEX with Decrement on a Unique Column.
Using Oracle SQL Developer, below is a sample table: I want the record with the most recent ORDER_DATE to be on top. Note: The Date is stored as a string. I also tried using REVERSE on the Primary Key column but that did not do it.
CREATE TABLE ORDERS
(
ORDER_NBR NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY
INCREMENT BY 10
MAXVALUE 9999999999999999999999999999
MINVALUE 135790864211
CACHE 20 NOT NULL
, CUSTOMER_ID NUMBER NOT NULL
, ORDER_TYPE NUMBER NOT NULL
, ORDER_DATE NVARCHAR2(27) NOT NULL
, RETURN_DATE NVARCHAR2(27)
, CONSTRAINT PK_ORDER_NBR_ORDERS PRIMARY KEY
(
ORDER_NBR
));
CREATE UNIQUE INDEX IDX_ORDER_DATE_ORDERS ON ORDERS (ORDER_DATE DESC);
CREATE INDEX IDX_RETURN_DATE_ORDERS ON ORDERS (RETURN_DATE DESC);

Resources