October 13, 2011 at 3:27 am
I have a contraint working well, where the sum of two column values must not exceed a constant.
Requirements have change. Now the constraint must include multiple rows.
USE TempDB
GO
CREATE TABLE ConstraintCheck (
pKey INT IDENTITY(1,1),
DDate smalldatetime,
Dtype smallINT,
Value1 smallInt,
Value2 smallInt )
-- Existing Constraint
ALTER TABLE ConstraintCheck
ADD CONSTRAINT chkTotalValues CHECK ((Value1 + Value2) <= 999);
INSERT INTO ConstraintCheck (DDate, Dtype, Value1, value2)
SELECT '2011-10-12', 1, 599, 0 UNION ALL
SELECT '2011-10-12', 2, 300, 200 UNION ALL
SELECT '2011-10-13', 1, 0, 600 UNION ALL
SELECT '2011-10-13', 2, 300, 0;
-- Constraint works. this update fails
UPDATE ConstraintCheck
SET Value1 = 1000
WHERE DDate = '2011-10-12'
-- However the requirement change means the constraint should be across all types
-- for a particular day. Here DayTotal must not exceed 999
SELECT DDate, SUM(Value1 + Value2) AS DayTotal
FROM ConstraintCheck
GROUP BY DDate
How can I implement this constraint?
October 13, 2011 at 3:37 am
As far as I know, a constraint only applies to a single row. In order to do what you require, I think you'll need to use a trigger.
John
October 13, 2011 at 3:49 am
October 13, 2011 at 4:42 am
Thanks,
Interesting blog. I've tried to created the indexed view as I was hoping to avoid the trigger route (for reasons expressed in Gianluca's blog), however I'm getting errors in creating one.
CREATE TABLE dbo.TwoRows( N INT NOT NULL PRIMARY KEY );
INSERT INTO TwoRows VALUES(1);
INSERT INTO TwoRows VALUES(2);
GO
CREATE VIEW dbo.vCHECK_Constraints
WITH SCHEMABINDING
AS
SELECT 1 AS ONE
FROM dbo.ConstraintCheck AS C
CROSS JOIN dbo.TwoRows AS TR
GROUP BY DDate
HAVING SUM(C.Value1 + C.Value2) > 999
GO
CREATE UNIQUE CLUSTERED INDEX IX_vCHECK_Constraints
ON dbo.vCHECK_CONSTRAINTS(ONE);
Error is
"Msg 10121, Level 16, State 1, Line 1
Cannot create index on view "tempdb.dbo.vCHECK_Constraints" because it contains a HAVING clause. Consider removing the HAVING clause."
But If I change the HAVING to WHERE I get another error
"Msg 147, Level 15, State 1, Line 1
An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference."
Is there a way around this
October 13, 2011 at 6:55 am
Darn! I guess you're out of luck.
I tried in many ways, but I think that this particular requirement can't be implemented with an indexed view.
Some restrictions apply, and one of those is the absence of the HAVING clause.
I'm sorry for suggesting a wrong way.
-- Gianluca Sartori
October 13, 2011 at 7:12 am
you guys completely forgot that you can use a check constraint that uses a user defined function...and that UDF can check mutiple columns, rows, values, other tables, whatever.
i've done several posts that did things like guarantee that all rows for a particualr Id did not sum up more than 100, etc.
here's an example based off the original tables:
USE TempDB
GO
CREATE TABLE ConstraintCheck (
pKey INT IDENTITY(1,1),
DDate smalldatetime,
Dtype smallINT,
Value1 smallInt,
Value2 smallInt )
-- Existing Constraint
GO
--create a UDF to use for the constriant!
CREATE function dbo.Check999Values(@Value1 smallInt, @Value2 smallInt)
RETURNS smallint
AS
BEGIN
RETURN
CASE
WHEN (@Value1 + @Value2) <= 999
THEN 1
ELSE 0
END
END --Function
GO
ALTER TABLE ConstraintCheck
ADD CONSTRAINT chkTotalValues CHECK (dbo.Check999Values(Value1,Value2)=1);
INSERT INTO ConstraintCheck (DDate, Dtype, Value1, value2)
SELECT '2011-10-12', 1, 599, 0 UNION ALL
SELECT '2011-10-12', 2, 300, 200 UNION ALL
SELECT '2011-10-13', 1, 0, 600 UNION ALL
SELECT '2011-10-13', 2, 300, 0;
-- Constraint works. this update fails
UPDATE ConstraintCheck
SET Value1 = 1000
WHERE DDate = '2011-10-12'
/*
Msg 547, Level 16, State 0, Line 1
The UPDATE statement conflicted with the CHECK constraint "chkTotalValues". The conflict occurred in database "tempdb", table "dbo.ConstraintCheck".
The statement has been terminated.
*/
Lowell
October 13, 2011 at 7:23 am
Guess I'm resigned to writing the trigger. Thats not the problem, its justifying it to the DBA thats going to be the problem.
Still I learned something reading the article on indexed views, so thanks for that. 😀
October 13, 2011 at 7:30 am
Lowell (10/13/2011)
you guys completely forgot that you can use a check constraint that uses a user defined function...
No, I'm not forgetting, I'm deliberately avoiding that. 😛
From a performance standpoint, it can be very inefficient. Also, has an hidden pitfall: if the column(s) you modify are not explicitly used in the constraint definition, the constraint is not evaluated at all, even if it is not bound to a particular column (hence, as MS says, it is "table-scoped").
I blogged about it here[/url].
-- Gianluca Sartori
Viewing 8 posts - 1 through 7 (of 7 total)
You must be logged in to reply to this topic. Login to reply