You can do it with a trigger or with a CHECK constraint WITH NOCHECK.
Be aware that:
1) CHECK constraints can't read data from another table directly, but have to use a scalar function. From a performance standpoint, I see this even worse than triggers
2) If a CHECK constraint is attached to a column, it is not invoked if you update another column. Moreover, I just discovered a weird thing: if the column you update is not explicitly used in the CHECK constraint, you can bypass the check.
Example:
USE tempdb;
GO
-- Create master table
CREATE TABLE masterTable (
id int identity(1,1) PRIMARY KEY,
dateField datetime
)
GO
-- Create referenced table
CREATE TABLE referencedTable (
id int identity(1,1) PRIMARY KEY,
master_id int FOREIGN KEY REFERENCES masterTable(id),
valueField varchar(50)
)
GO
-- Insert sample data
INSERT INTO masterTable(dateField) VALUES(GETDATE())
INSERT INTO masterTable(dateField) VALUES(DATEADD(day,-1,GETDATE()))
INSERT INTO masterTable(dateField) VALUES(DATEADD(day,-2,GETDATE()))
SELECT * FROM masterTable
-- Insert sample data in referenced table
INSERT INTO referencedTable(master_id, valueField) VALUES (1,'Value for id 1')
INSERT INTO referencedTable(master_id, valueField) VALUES (2,'Value for id 2')
INSERT INTO referencedTable(master_id, valueField) VALUES (3,'Value for id 3')
SELECT * FROM referencedTable
GO
-- Create a scalar UDF: you will need this in the CHECK constraint
CREATE FUNCTION getMasterDate(@id int)
RETURNS datetime
AS
BEGIN
RETURN ISNULL((SELECT dateField FROM masterTable WHERE id = @id),'30110101')
END
GO
-- Add the constraint WITH NOCHECK: existing rows are not affected
ALTER TABLE referencedTable WITH NOCHECK ADD CONSTRAINT chk_date
CHECK (DATEADD(day,-1,GETDATE()) > dbo.getMasterDate(master_id))
-- Update referenced table. Some rows conflict with the CHECK constraint, but it works
-- because the constraint is attached to a single column
UPDATE referencedTable
SET valueField = 'New ' + valueField
-- If you try to update master_id it fails
UPDATE referencedTable
SET master_id = master_id + 1 - 1
--Msg 547, Level 16, State 0, Line 2
--The UPDATE statement conflicted with the CHECK constraint "chk_date". The conflict occurred in database "tempdb", table "dbo.referencedTable", column 'master_id'.
--The statement has been terminated.
-- Drop the constraint
ALTER TABLE referencedTable DROP CONSTRAINT chk_date
GO
-- Create the constraint referencing multiple columns
ALTER TABLE referencedTable WITH NOCHECK ADD CONSTRAINT chk_date
CHECK (
DATEADD(day,-1,GETDATE()) > dbo.getMasterDate(master_id)
AND
ISNULL(valueField,'') = ISNULL(valueField,SPACE(0))
)
GO
-- DOESN'T WORK ANY MORE:
UPDATE referencedTable
SET valueField = 'New ' + valueField
--Msg 547, Level 16, State 0, Line 1
--The UPDATE statement conflicted with the CHECK constraint "chk_date". The conflict occurred in database "tempdb", table "dbo.referencedTable".
--The statement has been terminated.
You can check the column/table scope of the constraint querying sys.check_constraints:
SELECT parent_column_id, definition, is_not_trusted
FROM sys.check_constraints
WHERE parent_object_id = OBJECT_ID('referencedTable')
parent_column_id = 0 means table scoped constraint, parent_column_id > 0 means a single column.
Hope this helps
Gianluca
-- Gianluca Sartori