The SQL Server 2005 manual actually says;
A unique index or constraint cannot be created if there are existing duplicate values in the key columns.
That said, you can work around it. The below is one way, if it's acceptable in your case is up to you; the example below has a reservation that I only have SQL Server 2008 to test on :)
What you can do is create a _dedupe
column and include that in the index. For existing duplicates you set unique values in the columns, leaving one row with a NULL
value. When inserting the further values, don't set the _dedupe
column, and you'll fail inserts which are duplicates.
As an example;
> CREATE TABLE test ( id INT, value INT );
> INSERT INTO test (id, value) VALUES (1,1),(2,1),(3,3);
id value
----------
1 1
2 1
3 3
> ALTER TABLE test ADD _dedupe INT;
-- Update, partition by the value combination that is not unique now but
-- should be later, in this case "value".
> WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY value ORDER BY id)-1 rn FROM test
)
UPDATE cte SET _dedupe = CASE WHEN rn=0 THEN NULL ELSE rn END;
id value _dedupe
--------------------
1 1 NULL
2 1 1
3 3 NULL
> CREATE UNIQUE INDEX uq_value ON test(value, _dedupe);
> INSERT INTO test (id, value) VALUES (4,1) <-- fail, not unique
One downside with this approach is that the only row that prevents a duplicate insert is the NULL
row, if you delete that you may end up with a new duplicate with an existing numbered row. That may or may not be a problem for your system.