Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Triggers for checking complex uniqueness for table with soft deletes

I have a table that contains information about permits for various regions.

It has unique identity primary key, and a two column unique key which is the application’s permit number and the region ID.

It also supports soft deletes; when a row is deleted it just sets a bit field.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

I’m struggling to enforce uniqueness with the DB.

Can’t use a UNIQUE constraint as it fails if a deleted item uses the permit number.
Can’t see a way to use a check constraint because I need to run a query to check for duplicates.

I have got this far with a trigger:

        create table Test (
            ID int identity primary key,
            PN nvarchar(25),
            RID int,
            Deleted bit default(0)
        )
        go
        create trigger T_PN on Test instead of insert 
        as
        begin
            declare @pn nvarchar(25), @rid int
            select @pn = PN, @rid = RID from inserted 

            if exists(select ID from Test where PN = @pn and RID = @rid and Deleted = 0)
                RAISERROR('PN in use', 11, 1);
            else
                insert into Test select * from inserted
        end

However this won’t work because of the identity column; An explicit value for the identity column in table 'Test' can only be specified when a column list is used and IDENTITY_INSERT is ON.

This project is an ongoing rapid development project; the table will change regularly so hardcoding the column names is not an option.

I don’t know the ramifications of using IDENTITY_INSERT within a trigger and it feels hacky and dangerous?

I’m leaning towards an app-layer solution, but am I missing anything?

>Solution :

Use a filtered unique index isntead:

CREATE TABLE dbo.Test (TestID int IDENTITY CONSTRAINT PK_test PRIMARY KEY, --ALWAYS name your constraints
                       PN nvarchar(25) NOT NULL,
                       RID int NOT NULL,
                       Deleted bit NOT NULL CONSTRAINT DF_Test_Deleted DEFAULT(0)); ---ALWAYS name your constraints

GO

CREATE UNIQUE INDEX IX_Test_PN_RID_Active ON dbo.Test (PN,RID) WHERE Deleted = 0;

Then you can’t INSERT (or UPDATE) a row to be the same as an existing "undeleted" row:

--All work
INSERT INTO dbo.Test (PN,RID)
VALUES(N'abc',2),
      (N'abc',3),
      (N'def',3),
      (N'xyz',99);
GO
--Fails
INSERT INTO dbo.Test (PN,RID)
VALUES(N'abc',2);
GO
SELECT *
FROM dbo.Test;
GO

UPDATE dbo.Test
SET Deleted = 1
WHERE TestID = 1;
GO
--Now succeeds
INSERT INTO dbo.Test (PN,RID)
VALUES(N'abc',2);
GO
--Fails, it's the same as the row we just inserted
UPDATE dbo.Test
SET RID = 2
WHERE TestID = 2;
GO

SELECT *
FROM dbo.Test;
GO
--Clean up
DROP TABLE dbo.Test;
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading