17

I have a table and I need to restrict the data inserted, I already use a trigger but I was wondering if I could do the same task with a constraint.

The fields are:

Id
Date
Passport
...
Deleted

The constraint must allow n records while the Deleted field is 1, but if the Deleted field is 0 there must be only one row with the same Date and Passport.

Should work for this steps:

  1. Adding a row with Id=1, Date=2018-05-01, Passport=MPEUIE80, Deleted=0
  2. Deleting the row with Id=1, so the field Deleted will be 1
  3. Adding a row with Id=2, Date=2018-05-01, Passport=MPEUIE80, Deleted=0
  4. Deleting the row with Id=2, so the field Deleted will be 1
  5. Adding a row with Id=3, Date=2018-05-01, Passport=MPEUIE80, Deleted=0
  6. Adding a row with Id=4, Date=2018-05-01, Passport=MPEUIE80, Deleted=0

Till the fifth step, everything works, but in the last step there must be an error, that is because I can't handle two rows with the same Date, same Passport and both with Deleted=0

Thanks in advance.

7
  • ``The constraint must allow n records while the Deleted field is 1, but if the Deleted field is 0 there must be one Date and Passport`. Please explain this more clearly Commented May 4, 2018 at 19:14
  • 2
    Kind of smells like something is off in the design if you have one field representing multiple relationships. You can search for something like "Case statement with constraint", but I think a re-evaluation of the structure might be a better option. Commented May 4, 2018 at 19:18
  • You created a bounty but Gordon gave you an answer? What don't you like about the index? Commented May 11, 2018 at 15:52
  • @DerrickMoeller That answer didn't work. Commented May 11, 2018 at 16:37
  • Interesting question 8) Commented May 11, 2018 at 18:47

1 Answer 1

25

You can use a filtered, unique index:

create table [t](
    [id] [int] NULL,
    [date] [datetime] NULL,
    [passport] [char](8) NULL,
    [deleted] [int] NULL
)

create unique index unq_t_date_passport on t(date, passport)
    where deleted = 0;

EDIT:

If filtered indexes are not working on your version of SQL Server, then you might want to check the compatibility level. It should be available whenever the compatibility is set to 100 or higher. You can check your server version and compatibility level using:

SELECT SERVERPROPERTY('ProductVersion');
SELECT name, compatibility_level FROM sys.databases;

There is another method that will work starting in SQL Server 2005, assuming the id is unique and non-negative. It uses a computed column to do essentially the same thing as the filtered index:

alter table t
    add column deleted_id (case when deleted = 0 then -1 else id end) persisted;

create unique index unq_t_passport_date_deleted_id on t(passport, date, deleted_id);
Sign up to request clarification or add additional context in comments.

7 Comments

This didn't work for me, error: Incorrect syntax near the keyword 'where'.
@WltrRpo Did you change t to the name of your table?
@DerrickMoeller yes I did, create unique index unq_t_date_passport on [dbo].[Test] ([date], [passport]) where deleted = 0;
@WltrRpo . . . Filtered indexes were introduced in SQL Server 2008 (learn.microsoft.com/en-us/sql/t-sql/statements/…). If they don't work for you, perhaps you have your compatibility level set to some earlier version (learn.microsoft.com/en-us/sql/t-sql/statements/…).
Shouldnt the 2005 solution be reversed? case when deleted = 1 then id else -1 end, otherwise the unique index is preventing multiple deleted rows - also should handle the date field to be complete
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.