views:

60

answers:

3

Hi. I have a table, something like

FieldsOnForms(
 FieldID int (FK_Fields)
 FormID int (FK_Forms)
 isDeleted bit
)

The pair (FieldID,FormID) should be unique, BUT only if the row is not deleted (isDeleted=0).

Is it possible to define such a constraint in SQLServer 2008? (without using triggers)

P.S. Setting (FieldID, FormID, isDeleted) to be unique adds the possibility to mark one row as deleted, but i would like to have the chance to set n rows (per FieldID,FormID) to isDeleted = 1, and to have only one with isDeleted = 0

+7  A: 

You can have a unique index, using the SQL Server 2008 filtered indexes feature, or you can apply a UNIQUE index against a view (poor man's filtered index, works against earlier versions), but you cannot have a UNIQUE constraint such as you've described.

An example of the filtered index:

 CREATE UNIQUE NONCLUSTERED INDEX IX_FieldsOnForms_NonDeletedUnique ON FieldsOnForms (FieldID,FormID) WHERE isDeleted=0
Damien_The_Unbeliever
+1 best answer...
gbn
Althought I'm having some error, because of the bit field(Converting a column to the data type of a constant is not supported for filtered indexes), this seems to be the solution that I was loking for.
jaraics
@jaraics - I've just created the table in your question, and then the index in my answer, and it's created without an error. Also, if you are encountering data type mismatches, it's usually better to CAST (or CONVERT) the constant to the column's type, rather than the other way around. Not sure if that would fix your issue.
Damien_The_Unbeliever
I forgot to mention that isDeleted has a default value of 0, maybe that's the difference. If I cast(0 as bit) then the index is created, but i can't insert from the SSMS any more (but inserting from script works)
jaraics
@jaraics - it's time to stop relying on SSMS for the crutches it provides, and walk on your own :-)
Damien_The_Unbeliever
+1  A: 

No, unique means really unique. You'll either have to move your deleted records to another table or change IsDeleted to something that can be unique across all deleted records (say a time stamp). Either solution will require additional work either in your application, in a stored procedure, or in a trigger.

Larry Lustig
+2  A: 

You could change your IsDeleted column to a DeletedDate and make it a DATETIME with the exact time when the row was logically deleted. Alternatively, you could add a DeletedDate column and then create an IsDeleted computed column against that so that you still have that column available if it's being used in code. You would then of course put a unique index over the DeletedDate (in addition to the FieldID and FormId) instead of the IsDeleted column. That would allow exactly one NULL column.

Albin posted a solution similar to this, but then deleted it. I'm not sure why, but if he re-posts it then his was here before mine. :)

Tom H.
This seems to be a good alternaive for earlier SQL servers.
jaraics