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
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