Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

conditional unique constraint

I have a situation where i need to enforce a unique constraint on a set of columns, but only for one value of a column.

So for example I have a table like Table(ID, Name, RecordStatus).

RecordStatus can only have a value 1 or 2 (active or deleted), and I want to create a unique constraint on (ID, RecordStatus) only when RecordStatus = 1, since I don't care if there are multiple deleted records with the same ID.

Apart from writing triggers, can I do that?

I am using SQL Server 2005.

like image 696
np-hard Avatar asked May 14 '09 21:05

np-hard


People also ask

What is unique constraint with example?

The UNIQUE Constraint prevents two records from having identical values in a column. In the CUSTOMERS table, for example, you might want to prevent two or more people from having an identical age.

What are unique constraints in SQL?

The UNIQUE constraint ensures that all values in a column are different. Both the UNIQUE and PRIMARY KEY constraints provide a guarantee for uniqueness for a column or set of columns. A PRIMARY KEY constraint automatically has a UNIQUE constraint.

Is unique a constraint or unique?

A unique index ensures that the values in the index key columns are unique. A unique constraint also guarantees that no duplicate values can be inserted into the column(s) on which the constraint is created. When a unique constraint is created a corresponding unique index is automatically created on the column(s).

How do you declare a unique constraint?

The syntax for creating a unique constraint using an ALTER TABLE statement in SQL Server is: ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE (column1, column2, ... column_n); table_name.


2 Answers

Behold, the filtered index. From the documentation (emphasis mine):

A filtered index is an optimized nonclustered index especially suited to cover queries that select from a well-defined subset of data. It uses a filter predicate to index a portion of rows in the table. A well-designed filtered index can improve query performance as well as reduce index maintenance and storage costs compared with full-table indexes.

And here's an example combining a unique index with a filter predicate:

create unique index MyIndex on MyTable(ID) where RecordStatus = 1;

This essentially enforces uniqueness of ID when RecordStatus is 1.

Following the creation of that index, a uniqueness violation will raise an arror:

Msg 2601, Level 14, State 1, Line 13
Cannot insert duplicate key row in object 'dbo.MyTable' with unique index 'MyIndex'. The duplicate key value is (9999).

Note: the filtered index was introduced in SQL Server 2008. For earlier versions of SQL Server, please see this answer.

like image 152
canon Avatar answered Sep 18 '22 23:09

canon


Add a check constraint like this. The difference is, you'll return false if Status = 1 and Count > 0.

http://msdn.microsoft.com/en-us/library/ms188258.aspx

CREATE TABLE CheckConstraint (   Id TINYINT,   Name VARCHAR(50),   RecordStatus TINYINT ) GO  CREATE FUNCTION CheckActiveCount(   @Id INT ) RETURNS INT AS BEGIN    DECLARE @ret INT;   SELECT @ret = COUNT(*) FROM CheckConstraint WHERE Id = @Id AND RecordStatus = 1;   RETURN @ret;  END; GO  ALTER TABLE CheckConstraint   ADD CONSTRAINT CheckActiveCountConstraint CHECK (NOT (dbo.CheckActiveCount(Id) > 1 AND RecordStatus = 1));  INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2); INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2); INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2); INSERT INTO CheckConstraint VALUES (1, 'No Problems', 1);  INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1); INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 2); -- Msg 547, Level 16, State 0, Line 14 -- The INSERT statement conflicted with the CHECK constraint "CheckActiveCountConstraint". The conflict occurred in database "TestSchema", table "dbo.CheckConstraint". INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1);  SELECT * FROM CheckConstraint; -- Id   Name         RecordStatus -- ---- ------------ ------------ -- 1    No Problems  2 -- 1    No Problems  2 -- 1    No Problems  2 -- 1    No Problems  1 -- 2    Oh no!       1 -- 2    Oh no!       2  ALTER TABLE CheckConstraint   DROP CONSTRAINT CheckActiveCountConstraint;  DROP FUNCTION CheckActiveCount; DROP TABLE CheckConstraint; 
like image 41
D. Patrick Avatar answered Sep 20 '22 23:09

D. Patrick