Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
LC
Starting Member
7 Posts |
Posted - 2011-09-23 : 18:23:31
|
| This question is essentially about how to make primary and foreign keys work correctly with temporal data.If I have a table that looks like this:create table parent ( StartDate date, EndDate date, Code int, Value varchar(255), primary key(StartDate, EndDate, Code))And a couple of example rows:insert into parent values('2011/01/01','2011/06/30',1,'The value from Jan-Jun')insert into parent values('2011/07/01','2011/12/31',1,'The value from Jul-Dec')And a table that looks like this:create table child ( TheKey int, TheDate date, Code int, primary key (TheKey))and a sample row:insert into child values(1,'2011/09/23',1)Then TheDate in the child table selects the correct row in the parent table:select c.TheKey, c.TheDate, c.Code, p.Valuefrom child cinner join parent pon c.Code = p.Codeand c.TheDate between p.StartDate and p.EndDateThis design has problems with the enforcement of integrity. For one, I can do overlapping date ranges:insert into parent values('2011/03/01','2011/11/30',2,'The value from Mar-Nov')insert into parent values('2011/07/01','2011/12/31',2,'The value from Jul-Dec')insert into child values(2,'2011/09/23',2)Row 2 in the child table matches on two rows in the parent table, not just one. Is there a way to enforce a primary key on the parent table where the dates can't overlap for the same Code?Second question, is there a way to declare a FK so that any date in the range will be valid?Thanks... |
|
|
ehorn
Master Smack Fu Yak Hacker
1632 Posts |
Posted - 2011-09-23 : 18:44:22
|
quote: Originally posted by LC...Is there a way to enforce a primary key on the parent table where the dates can't overlap for the same Code?Second question, is there a way to declare a FK so that any date in the range will be valid?Thanks...
One option is to construct scalar UDF's to implement your checks and then assign them as CHECK CONSTRAINT's on your table(s).HTH. |
 |
|
|
ehorn
Master Smack Fu Yak Hacker
1632 Posts |
Posted - 2011-09-23 : 20:49:03
|
quote: Originally posted by ehorn
quote: Originally posted by LC...Is there a way to enforce a primary key on the parent table where the dates can't overlap for the same Code?Second question, is there a way to declare a FK so that any date in the range will be valid?Thanks...
One option is to construct scalar UDF's to implement your checks and then assign them as CHECK CONSTRAINT's on your table(s).HTH.
Something like this;--*************************************************************************-- Parent Table CHECK CONSTRAINTS--*************************************************************************--udf to perform the checkCREATE FUNCTION dbo.udfIsValidDateRange( @StartDate DATE, @EndDate DATE, @Code INT)RETURNS BITASBEGIN DECLARE @isValid BIT SET @isValid = 1 IF EXISTS ( SELECT 1 FROM dbo.parent WHERE Code = @Code AND ( @StartDate != StartDate OR @EndDate != EndDate) AND ( (@StartDate > StartDate AND @StartDate < EndDate) OR (@EndDate > StartDate AND @EndDate < EndDate) ) ) SET @isValid = 0 RETURN @isValidENDGO--table with constraints appliedCREATE TABLE dbo.parent ( StartDate DATE, EndDate DATE, Code INT, Value VARCHAR(255), PRIMARY KEY(StartDate, EndDate, Code), CHECK ( StartDate < EndDate ), CONSTRAINT ck_IsValidDateRange CHECK (dbo.udfIsValidDateRange(StartDate, EndDate, Code) = 1))GO--pass of Code=1INSERT INTO dbo.parent VALUES('2011/01/01','2011/06/30',1,'The value from Jan-Jun') --wors fineINSERT INTO dbo.parent VALUES('2011/07/01','2011/12/31',1,'The value from Jul-Dec') --works fine--fail for Code=1INSERT INTO dbo.parent VALUES('2011/05/01','2011/06/30',1,'The value from May-Jun') --fails overlap in startdate and enddate--pass for Code=2INSERT INTO dbo.parent VALUES('2011/03/01','2011/10/30',2,'The value from Mar-Oct') --work fineINSERT INTO dbo.parent VALUES('2011/01/01','2011/03/01',2,'The value from Jan-Mar') --work fine--fail for Code=2INSERT INTO dbo.parent VALUES('2011/07/01','2011/12/31',2,'The value from Jul-Dec') --fails overlap in startdateINSERT INTO dbo.parent VALUES('2011/02/01','2011/04/20',2,'The value from Feb-Apr') --fails overlap in enddateSELECT * FROM parent--*************************************************************************-- Child Table CHECK CONSTRAINTS--*************************************************************************--udf to perform the date checkCREATE FUNCTION dbo.udfIsValidDate( @TheDate DATE, @Code INT)RETURNS BITASBEGIN DECLARE @isValid BIT SET @isValid = 0 IF EXISTS ( SELECT 1 FROM dbo.parent WHERE Code = @Code AND @TheDate >= StartDate AND @TheDate <= EndDate ) SET @isValid = 1 RETURN @isValidENDGO--udf to perform the Code checkCREATE FUNCTION dbo.udfIsValidCode( @Code INT)RETURNS BITASBEGIN DECLARE @isValid BIT SET @isValid = 0 IF EXISTS ( SELECT 1 FROM dbo.parent WHERE Code = @Code ) SET @isValid = 1 RETURN @isValidENDGOCREATE TABLE dbo.child ( TheKey INT PRIMARY KEY, TheDate DATE, Code INT, CONSTRAINT ck_Code CHECK (dbo.udfIsValidCode(Code) = 1), CONSTRAINT ck_IsValidDate CHECK (dbo.udfIsValidDate(TheDate, Code) = 1))--passes for Code=1INSERT INTO child values(1,'2011/09/23',1)INSERT INTO child values(2,'2011/11/05',1)--fails for Code=1INSERT INTO child values(3,'2010/09/23',1)--passes for Code=2INSERT INTO child values(4,'2011/09/23',2)--fails for Code=2INSERT INTO child values(5,'2010/12/31',2)--fails for Code=3 (not found in parent)INSERT INTO child values(5,'2010/12/31',3)SELECT * FROM child--DROP TABLE dbo.parent, dbo.child--DROP FUNCTION dbo.udfIsValidDateRange, dbo.udfIsValidDate |
 |
|
|
ehorn
Master Smack Fu Yak Hacker
1632 Posts |
Posted - 2011-09-23 : 21:04:09
|
| If this method was emplyed to check date ranges, you could consider changing your composite key selection on parent table to say; [Code] only. This will allow you to have a relationship with child on [Code]. and then not require the additional check constraint on child(ck_Code). Just some thoughts for your consideration.Hope that helps stimulate some thought/options for you.Have a nice evening. |
 |
|
|
LC
Starting Member
7 Posts |
Posted - 2011-09-26 : 16:36:35
|
quote: Originally posted by ehorn If this method was emplyed to check date ranges, you could consider changing your composite key selection on parent table to say; [Code] only. This will allow you to have a relationship with child on [Code]. and then not require the additional check constraint on child(ck_Code). Just some thoughts for your consideration.Hope that helps stimulate some thought/options for you.Have a nice evening.
Thanks, but Code by itself isn't unique, so I can't limit the PK to just the code without at least one of the date values. |
 |
|
|
LC
Starting Member
7 Posts |
Posted - 2011-09-26 : 16:39:09
|
| Thanks for the info on the check constraint calling a scalar function. I figured that'd be they way it'd have to be done, but was hoping SQL Server had a more graceful way of handling temporal data. |
 |
|
|
|
|
|
|
|