, . CHECK FKs ( -) , , , :
create table dbo.Parents (
ParentID int IDENTITY(1,1) not null,
ValidFrom datetime not null,
ValidTo datetime not null,
CONSTRAINT PK_dbo_Parents PRIMARY KEY (ParentID),
CONSTRAINT UQ_dbo_Parents_DRI UNIQUE (ParentID, ValidFrom, ValidTo),
CONSTRAINT CK_dbo_Parents_ValidDates CHECK (ValidFrom <= ValidTo)
)
go
alter table dbo.Parents add constraint DF_dbo_Parents_ValidFrom DEFAULT (CURRENT_TIMESTAMP) for ValidFrom
go
alter table dbo.Parents add constraint DF_dbo_Parents_ValidTo DEFAULT (CONVERT(datetime,'99991231')) for ValidTo
go
create table dbo._Children (
ChildID int IDENTITY(1,1) not null,
ParentID int not null,
ChildDate datetime not null,
_ParentValidFrom datetime not null,
_ParentValidTo datetime not null,
CONSTRAINT PK_dbo__Children PRIMARY KEY (ChildID),
CONSTRAINT FK_dbo__Children_Parents FOREIGN KEY (ParentID,_ParentValidFrom,_ParentValidTo) REFERENCES dbo.Parents (ParentID,ValidFrom,ValidTo) ON UPDATE CASCADE,
CONSTRAINT CK_dbo__Children_ValidDate CHECK (_ParentValidFrom <= ChildDate and ChildDate < _ParentValidTo)
)
go
alter table dbo._Children add constraint DF_dbo__Children_ChildDate DEFAULT (CURRENT_TIMESTAMP) for ChildDate
go
create view dbo.Children (ChildID,ParentID,ChildDate)
with schemabinding
as
select ChildID,ParentID,ChildDate from dbo._Children
go
create trigger dbo.T_Children_I on dbo.Children instead of insert
as
begin
set nocount on
insert into dbo._Children (ParentID,ChildDate,_ParentValidFrom,_ParentValidTo)
select i.ParentID,i.ChildDate,p.ValidFrom,p.ValidTo
from
inserted i
inner join
dbo.Parents p
on
i.ParentID = p.ParentID
end
go
create trigger dbo.T_Children_U on dbo.Children instead of update
as
begin
set nocount on
if UPDATE(ChildID)
begin
RAISERROR('Updates to ChildID are not allowed',16,1)
return
end
update c
set
ParentID = i.ParentID,
ChildDate = i.ChildDate,
_ParentValidFrom = p.ValidFrom,
_ParentValidTo = p.ValidTo
from
inserted i
inner join
dbo._Children c
on
i.ChildID = c.ChildID
inner join
dbo.Parents p
on
i.ParentID = p.ParentID
end
go
insert into dbo.Parents(ValidFrom,ValidTo)
select '20081201','20090101' union all
select '20090201','20090301'
go
insert into dbo.Children (ParentID,ChildDate)
select 1,'20081215'
go
insert into dbo.Children (ParentID,ChildDate)
select 1,'20090115'
go
update dbo.Parents set ValidTo = '20090201' where ParentID = 1
go
insert into dbo.Children (ParentID,ChildDate)
select 1,'20090115'
go
update dbo.Parents set ValidTo = '20090101' where ParentID = 1
go
insert into dbo.Children (ParentID,ChildDate)
select 2,'20090215'
go
update dbo.Children set ChildDate = '20090115' where ParentID=2 and ChildDate = '20090215'
go
delete from dbo.Children
go
drop view dbo.Children
drop table dbo._Children
drop table dbo.Parents
go
This is for SQL Server. Tested in 2005, but should work at least 2000 and 2008. The bonus here is that even if the trigger is disabled (for example, nested triggers are disabled), you cannot end up with the wrong data in the base tables