MSSQL: Update statement avoiding the CHECK constraint

前端 未结 2 578
感情败类
感情败类 2020-12-18 07:41

Working in MS2000, I have a table called JobOwners that maps Jobs (JPSID) to the Employees that own them (EmpID). It also contains the date they started owning that job (Dat

2条回答
  •  春和景丽
    2020-12-18 08:28

    EDIT: HUGE caveat. See Aaron's comment on this SO question for reasons you probably want to avoid combining UDFs and CHECK CONSTRAINTS. However, since (even after reading and understanding Aaron's concerns) my answer is still viable in our system because of 1) how our system works and 2) we actually want UPDATE statements to fail in the scenarios he describes, I am leaving my answer here. As it ALWAYS is, it is up to you to make sure you understand the ramifications of using the script in this answer. YOU HAVE BEEN WARNED

    I followed the link in Aaron's (accepted) answer. In the description there was a specific piece of text that caught my attention "(to check values that are not passing as parameters)".

    That gave me an idea. I have a table with columns CustomerId, ContactId, ContactType all of type "int". The PK is CustomerId and ContactId. I needed to be able to limit each CustomerId to only have one "Primary" Contact (ContactType = 1) but as many "secondary" and "other" contacts as people wanted to add. I had setup my UDF to accept only CustomerId as a parameter. So, I added ContactType as well but since I only cared about ContactType = 1, I just hard-coded the ContactType parameter to 1 inside the function. It worked on SQL2012 but I have no idea about other versions.

    Here is a test script. I "squished" together some of the statements to reduce the amount of scrolling needed. Note: the constraint ALLOWS zero Primary Contacts because it would be impossible to set a different Contact as the Primary if you did not first remove an existing Primary.

    CREATE TABLE [dbo].[CheckConstraintTest](
        [CustomerId] [int] NOT NULL,
        [ContactId] [int] NOT NULL,
        [ContactType] [int] NULL,
    CONSTRAINT [PK_CheckConstraintTest] PRIMARY KEY CLUSTERED (
        [CustomerId] ASC,
        [ContactId] ASC
    ))
    GO
    
    CREATE FUNCTION dbo.OnlyOnePrimaryContact (
        @CustId int, @ContactType int ) RETURNS bit
    AS BEGIN
        DECLARE @result bit, @count int
        SET @ContactType = 1 --only care about "1" but needed parm to force SQL to "care" about that column
        SELECT @count = COUNT(*) FROM CheckConstraintTest WHERE [CustomerId] = @CustId AND [ContactType] = @ContactType
        IF @count < 2 SET @result = 1
        ELSE  SET @result = 0
        RETURN @result
    END
    GO
    
    ALTER TABLE [dbo].[CheckConstraintTest] WITH CHECK ADD CONSTRAINT [SinglePrimaryContact] CHECK  (([dbo].[OnlyOnePrimaryContact]([CustomerId],[ContactType])=(1)))
    GO
    
    ALTER TABLE [dbo].[CheckConstraintTest] CHECK CONSTRAINT [SinglePrimaryContact]
    GO
    
    INSERT INTO [CheckConstraintTest] (CustomerId, ContactId, ContactType) 
    VALUES (1,1,1), (1,2,2), (1,3,2), (1,4,2), (2,1,1)
    
    INSERT INTO [CheckConstraintTest] (CustomerId, ContactId, ContactType) 
    VALUES (1,5,1) --This should fail
    
    UPDATE [CheckConstraintTest] --This should fail
    SET ContactType = 1
    WHERE CustomerId = 1 AND ContactId = 2
    
    UPDATE [CheckConstraintTest] --This should work
    SET ContactType = 2
    WHERE CustomerId = 1 AND ContactId = 1
    
    INSERT INTO [CheckConstraintTest] (CustomerId, ContactId, ContactType) 
    VALUES (1,5,1) --This should work now since we change Cust 1, Contact 1, to "secondary" in previous statement
    

提交回复
热议问题