I am working in a project where database items are not deleted, but only marked as deleted. Something like this:
id name deleted
--- ------- --------
For a simple table called [Test] with columns ID(int), Filter(nvarchar), Deleted(bit)
ALTER TABLE [dbo].[Test] ADD
CONSTRAINT [DF_Test_Deleted] DEFAULT (0) FOR [Deleted],
CONSTRAINT [IX_Test] UNIQUE NONCLUSTERED
(
[filter],
[Deleted]
) ON [PRIMARY]
The type of constraint you require is a table-level CHECK
constraint i.e. a CHECK constraint consisting of a subquery which tests NOT EXISTS
(or equivalent) for the table e.g.
CREATE TABLE Test
(
ID INTEGER NOT NULL UNIQUE,
name VARCHAR(30) NOT NULL,
deleted INTEGER NOT NULL,
CHECK (deleted IN (0, 1))
);
ALTER TABLE Test ADD
CONSTRAINT test1__unique_non_deleted
CHECK
(
NOT EXISTS
(
SELECT T1.name
FROM Test AS T1
WHERE T1.deleted = 0
GROUP
BY T1.Name
HAVING COUNT(*) > 1
)
);
INSERT INTO Test (ID, name, deleted) VALUES (1, 'Thingy1', 0)
;
INSERT INTO Test (ID, name, deleted) VALUES (2, 'Thingy2', 0)
;
INSERT INTO Test (ID, name, deleted) VALUES (3, 'Thingy3', 1)
;
INSERT INTO Test (ID, name, deleted) VALUES (4, 'Thingy3', 1)
;
INSERT INTO Test (ID, name, deleted) VALUES (5, 'Thingy3', 0)
;
INSERT INTO Test (ID, name, deleted) VALUES (6, 'Thingy3', 0)
;
The last INSERT
(ID = 6) will cause the constraint to bite and the INSERT
will fail. Q.E.D.
...ooh, nearly forgot to mention: SQL Server doesn't yet support CHECK
constraints with that contain a subquery (I tested the above on ACE/JET, a.k.a. ). While you could use a FUNCTION
I've read this is unsafe due to SQL Server testing constraints on a row-by-row basis (see David Portas' Blog). Until this full SQL-92 feature is supported in SQL Server, my preferred workaround is to use the same logic in a trigger.
Not sure about SQLServer2005, but you can define compound constrainst/indexes
CREATE UNIQUE INDEX [MyINDEX] ON [TABLENAME] ([NAME] , [DELETED])
As pointed out by SteveWeet, this will only allow you to delete/create twice.
It might be worth considering using a "recycle bin" table. Instead of keeping the old records in the same table with a flag, move them to its own table with its own constraints. For instance, in the active table you do have a UNIQUE constraint on name, but in the recycle bin table you don't.