Prevent creating records if certain other records already exist

  • This is obviously doable by restricting adds to stored procedures, but I'd like to do it with a constraint, or index or some other such construct "built in" to the structure, rather than relying on code to run.

    I have three fields of concern - a required letter and a number, and an additional optional letter. Records must be unique across these three fields, which I handle with an index - no problem. However, it is permissible for there to be a record that has only a letter and number, OR for there to be one or more records that all have the same letter and number, and unique additional letter, but NOT both.

    Once there is a record with only letter and number, trying to add a record with a letter, number and additional letter should fail.

    Likewise, once there is at least one record with letter, number and additional letter, trying to add a record with only letter and number should fail.

    e.g.

    Okay

    E 41

    Okay

    E 41 a

    E 41 b

    E 41 c

    NOT Okay

    E 41

    E 41 a

    E 41 b

    E 41 c

    Can anyone think of a way to define this restriction as a constraint, index or something similar?

  • What is the business need to use a schema object to prevent this?

    Brandie Tarvin, MCITP Database AdministratorLiveJournal Blog: http://brandietarvin.livejournal.com/[/url]On LinkedIn!, Google+, and Twitter.Freelance Writer: ShadowrunLatchkeys: Nevermore, Latchkeys: The Bootleg War, and Latchkeys: Roscoes in the Night are now available on Nook and Kindle.

  • Brandie Tarvin (12/11/2014)


    What is the business need to use a schema object to prevent this?

    I didn't specify schema, and in fact, have no idea how a schema could be used to accomplish it. If you know of a way, I'd like to hear it. The business need is that it is not acceptable information. An item in inventory is either specified as a whole, or its individual components are listed separately, but not both.

  • pdanes (12/11/2014)


    Brandie Tarvin (12/11/2014)


    What is the business need to use a schema object to prevent this?

    I didn't specify schema, and in fact, have no idea how a schema could be used to accomplish it. If you know of a way, I'd like to hear it. The business need is that it is not acceptable information. An item in inventory is either specified as a whole, or its individual components are listed separately, but not both.

    Indexes, Constraints, etc. are all schema objects. I don't understand why the business would require using them for this particular situation. Could you please enlighten me?

    Brandie Tarvin, MCITP Database AdministratorLiveJournal Blog: http://brandietarvin.livejournal.com/[/url]On LinkedIn!, Google+, and Twitter.Freelance Writer: ShadowrunLatchkeys: Nevermore, Latchkeys: The Bootleg War, and Latchkeys: Roscoes in the Night are now available on Nook and Kindle.

  • Brandie Tarvin (12/11/2014)


    pdanes (12/11/2014)


    Brandie Tarvin (12/11/2014)


    What is the business need to use a schema object to prevent this?

    I didn't specify schema, and in fact, have no idea how a schema could be used to accomplish it. If you know of a way, I'd like to hear it. The business need is that it is not acceptable information. An item in inventory is either specified as a whole, or its individual components are listed separately, but not both.

    Indexes, Constraints, etc. are all schema objects. I don't understand why the business would require using them for this particular situation. Could you please enlighten me?

    The business doesn't require a particular methodology. -I- want to do it that way. If I write stored procedures that check for this, someone else can write a different stored procedure, or even direct access, that doesn't. It's even possible that I would forget about the need for this restriction sometime in the future, and commit such an act myself. If I encode the restriction directly in the structure, nobody, including me, can get by it without specifically altering that restriction. This is much less likely to happen.

  • As much as it pains me to say this (because it could cause performance issues), I think your best bet is to put in an AFTER trigger to rollback the change if the recent insert violates your business rules.

    Brandie Tarvin, MCITP Database AdministratorLiveJournal Blog: http://brandietarvin.livejournal.com/[/url]On LinkedIn!, Google+, and Twitter.Freelance Writer: ShadowrunLatchkeys: Nevermore, Latchkeys: The Bootleg War, and Latchkeys: Roscoes in the Night are now available on Nook and Kindle.

  • Brandie Tarvin (12/11/2014)


    As much as it pains me to say this (because it could cause performance issues), I think your best bet is to put in an AFTER trigger to rollback the change if the recent insert violates your business rules.

    Thank you, a trigger occurred to me as well, but I was hoping someone sharper in SQL Server than I am might know of a better way. This database doesn't get hammered very hard, and reads are much more frequent than updates, so I doubt if performance would be an issue.

    Triggers are certainly capable of causing problems, but I don't agree that they should never be used. They're an extremely useful tool in some situations, and this may be just such a place.

    Thanks for looking at the problem.

  • Hi

    Check constraint with user defined function should work as well

  • Emil Bialobrzeski (12/11/2014)


    Hi

    Check constraint with user defined function should work as well

    Hm, that's an interesting thought, but how would you code it? The function would need the infro from the record I'm trying to add, say E and 41, to see if E 41 (%) already exists, or E and 41 and a, to see if E 41 (blank) already exists. It could detect an unacceptable combination once it's already in the table, but I don't see how to code such a function to prevent the combination from arising in the first place.

  • Don't be to harsh on me i just done it quickly while doing my other work. In the example below i'm assuming that if the optional letter does not exist there is a NULL in place. Unique row identifier (ID) is also required.

    CREATE TABLE Test (ID INT IDENTITY(1,1), Letter CHAR(1), Number INT, OptionalLetter CHAR(1))

    GO

    CREATE FUNCTION dbo.CKTest (@ID INT, @Letter CHAR(1), @Number INT, @OptionalLetter CHAR(1))

    RETURNS BIT

    AS

    BEGIN

    DECLARE @r BIT

    DECLARE @IDF INT

    SELECT @IDF = ID FROM Test WHERE ID != @ID AND Letter = @Letter AND Number = @Number AND OptionalLetter IS NULL

    IF @IDF IS NOT NULL

    SET @r = 1

    ELSE IF @IDF IS NULL

    BEGIN

    SELECT @IDF = ID FROM Test WHERE ID != @ID AND Letter = @Letter AND Number = @Number AND ISNULL(OptionalLetter,'') = ISNULL(@OptionalLetter,OptionalLetter)

    IF @IDF IS NOT NULL

    SET @r = 1

    ELSE

    SET @r = 0

    END

    RETURN @r

    END

    GO

    ALTER TABLE Test ADD CONSTRAINT CK_Test CHECK (dbo.CKTest(ID, Letter, Number, OptionalLetter) = 0)

    GO

  • Emil Bialobrzeski (12/11/2014)


    Don't be to harsh on me i just done it quickly while doing my other work. In the example below i'm assuming that if the optional letter does not exist there is a NULL in place.

    My apologies, I meant how does a function in a check constraint get the parameters from the record that I'm trying to add? I know how to code a lookup, given the parameters, but when I simply try to add a record, and I want the check constraint to prevent me from doing so, how does the check constraint get the info needed to do a lookup, in this case, the parameters @Letter, @Number and @OptionalLetter, from the incoming record?

  • Yes the parameters are from the incoming record while you do an insert

    ALTER TABLE Test ADD CONSTRAINT CK_Test CHECK (dbo.CKTest(ID, Letter, Number, OptionalLetter) = 0)

    EDIT:

    Use the code from my previous post, make some inserts to Test table, I just tested it and works as expected (i think :hehe:)

  • Emil Bialobrzeski (12/11/2014)


    Yes the parameters are from the incoming record while you do an insert

    ALTER TABLE Test ADD CONSTRAINT CK_Test CHECK (dbo.CKTest(ID, Letter, Number, OptionalLetter) = 0)

    EDIT:

    Use the code from my previous post, make some inserts to Test table, I just tested it and works as expected (i think :hehe:)

    Ok, I've got to get out of here right now, but I'll try it and let you know. Thanks.

  • Here's a simplified function with sample testing data and results:

    IF OBJECT_ID('dbo.Test') IS NOT NULL

    DROP TABLE dbo.Test

    CREATE TABLE dbo.Test (letter char(1), number int, optional_letter char(1))

    GO

    IF OBJECT_ID('dbo.Test__FN_optional_letter') IS NOT NULL

    DROP FUNCTION dbo.Test__FN_optional_letter

    GO

    CREATE FUNCTION dbo.Test__FN_optional_letter (@letter char(1), @number int, @optional_letter char(1))

    RETURNS BIT

    AS

    BEGIN

    RETURN (

    SELECT CASE

    WHEN @optional_letter = ' ' AND EXISTS(SELECT 1 FROM dbo.Test WHERE letter = @letter AND number = @number AND optional_letter <> '')

    THEN -1

    WHEN @optional_letter > ' ' AND EXISTS(SELECT 1 FROM dbo.Test WHERE letter = @letter AND number = @number AND optional_letter = ' ')

    THEN -1

    --?WHEN @optional_letter IS NULL AND ...?

    ELSE 0

    END

    )

    END

    GO

    ALTER TABLE dbo.Test ADD CONSTRAINT Test__CK_optional_letter CHECK(dbo.Test__FN_optional_letter(letter, number, optional_letter) = 0)

    GO

    PRINT '1'

    INSERT INTO dbo.Test VALUES('E', 41, '')

    PRINT '2'

    INSERT INTO dbo.Test VALUES('E', 41, 'a')

    TRUNCATE TABLE dbo.Test

    PRINT '3'

    INSERT INTO dbo.Test VALUES('E', 41, 'a')

    PRINT '4'

    INSERT INTO dbo.Test VALUES('E', 41, 'b')

    PRINT '5'

    INSERT INTO dbo.Test VALUES('E', 41, 'c')

    PRINT '6'

    INSERT INTO dbo.Test VALUES('E', 41, '')

    SQL DBA,SQL Server MVP(07, 08, 09) A socialist is someone who will give you the shirt off *someone else's* back.

  • @Brandie: The Business Case for this (and others like it) are BOM lists to make sure you don't 'double tap' an item in the hierarchy. Doing it at the schema level just avoids future coding from avoiding your well tailored procedure, but I'm sure you knew that.

    @scott: You missed a space in the first condition, near the end. Otherwise, that's a form of how I've done this in the past. However, we used NULL instead of ' '. Was more obvious and less prone to user errors.

    @pdanes: My usual technique to deal with this is a trigger. When the inserted table comes in, the values are checked against existing entries in the table. The reason I usually do this via trigger instead of schema controls is so I can raise custom errors to the front end. The unfortunate part of doing it this way is you need to check both the rest of the inserted table for competing entries as well as the storage table, so it's the long way around. As long as you're not expecting heavy writes against the system though, you should be fine. Scott's way above is also valid as long as you can handle schema error codes in your front end where the users are entering items and translate them to users properly.


    - Craig Farrell

    Never stop learning, even if it hurts. Ego bruises are practically mandatory as you learn unless you've never risked enough to make a mistake.

    For better assistance in answering your questions[/url] | Forum Netiquette
    For index/tuning help, follow these directions.[/url] |Tally Tables[/url]

    Twitter: @AnyWayDBA

Viewing 15 posts - 1 through 15 (of 22 total)

You must be logged in to reply to this topic. Login to reply