07 February 2008 10:07 Alex_Kuznetsov

Using a Foreign key constraint to check validity of History Windows (Start - End Date Windows)

CREATE TABLE dbo.TaskStatuses(TaskID INT NOT NULL,
  Status VARCHAR(20),
  StartedAt DATETIME NOT NULL,
  FinishedAt DATETIME NOT NULL,
  PreviousFinishedAt DATETIME NULL,
  CONSTRAINT PK_TaskStatuses_TaskID_FinishedAt PRIMARY KEY(TaskID, FinishedAt),
  CONSTRAINT UNQ_TaskStatuses_TaskID_PreviousFinishedAt UNIQUE(TaskID, PreviousFinishedAt),
  CONSTRAINT FK_TaskStatuses_TaskID_PreviousFinishedAt
    FOREIGN KEY(TaskID, PreviousFinishedAt)
    REFERENCES dbo.TaskStatuses(TaskID, FinishedAt),
  CONSTRAINT CHK_TaskStatuses_PreviousFinishedAt_Before_StartedAt CHECK(PreviousFinishedAt <= StartedAt)
)
go

INSERT INTO dbo.TaskStatuses(TaskID, Status, StartedAt, FinishedAt, PreviousFinishedAt)
  VALUES(1, 'Pending', '20070101', '20070103', NULL)

-- you cannot have more than one beginning of history chain per task:
INSERT INTO dbo.TaskStatuses(TaskID, Status, StartedAt, FinishedAt, PreviousFinishedAt)
  VALUES(1, 'Pending', '20070104', '20070105', NULL)
/*
Server: Msg 2627, Level 14, State 2, Line 1
Violation of UNIQUE KEY constraint 'UNQ_TaskStatuses_TaskID_PreviousFinishedAt'. Cannot insert duplicate key in object 'TaskStatuses'.
The statement has been terminated.
*/
INSERT INTO dbo.TaskStatuses(TaskID, Status, StartedAt, FinishedAt, PreviousFinishedAt)
  VALUES(1, 'Pending', '20070104', '20070105', '20070103')

-- history windows cannot overlap:
INSERT INTO dbo.TaskStatuses(TaskID, Status, StartedAt, FinishedAt, PreviousFinishedAt)
  VALUES(1, 'Opened', '20070104', '20070109', '20070105')
/*
Server: Msg 547, Level 16, State 1, Line 1
INSERT statement conflicted with TABLE CHECK constraint 'CHK_TaskStatuses_PreviousFinishedAt_Before_StartedAt'. The conflict occurred in database 'RiskCenter', table 'TaskStatuses'.
The statement has been terminated.
*/
INSERT INTO dbo.TaskStatuses(TaskID, Status, StartedAt, FinishedAt, PreviousFinishedAt)
  VALUES(1, 'Opened', '20070114', '20070119', '20070105')

-- you cannot fill a gap in one insert
-- Gap between Jan 5 and Jan 14

INSERT INTO dbo.TaskStatuses(TaskID, Status, StartedAt, FinishedAt, PreviousFinishedAt)
  VALUES(1, 'Reviewed', '20070105', '20070114', '20070105')
/*
Server: Msg 2627, Level 14, State 2, Line 1
Violation of UNIQUE KEY constraint 'UNQ_TaskStatuses_TaskID_PreviousFinishedAt'. Cannot insert duplicate key in object 'TaskStatuses'.
The statement has been terminated.
*/

-- to fill a gap, add to the end of the chain:
INSERT INTO dbo.TaskStatuses(TaskID, Status, StartedAt, FinishedAt, PreviousFinishedAt)
  VALUES(1, 'Reviewed', '20070125', '20070129', '20070119')

/*
TaskID      Status               StartedAt FinishedAt PreviousFinishedAt
----------- -------------------- --------- ---------- ------------------
1           Pending              20070101  20070103   NULL
1           Pending              20070104  20070105   20070103
1           Opened               20070114  20070119   20070105
1           Reviewed             20070125  20070129   20070119

(4 row(s) affected)
*/


-- then move the last period to fill the gap
UPDATE dbo.TaskStatuses SET StartedAt = CASE WHEN FinishedAt = '20070129' THEN '20070105' ELSE StartedAt END,
  FinishedAt = CASE WHEN FinishedAt = '20070129' THEN '20070114' ELSE FinishedAt END,  
  PreviousFinishedAt = CASE WHEN FinishedAt = '20070129' THEN '20070105' ELSE '20070114' END
WHERE TaskID = 1 AND FinishedAt IN('20070129', '20070119')

SELECT TaskID, Status, CONVERT(CHAR(8), StartedAt, 112) StartedAt,
    CONVERT(CHAR(8), FinishedAt, 112) FinishedAt,
    CONVERT(CHAR(8), PreviousFinishedAt, 112) PreviousFinishedAt
    FROM dbo.TaskStatuses ORDER BY FinishedAt

/*
TaskID      Status               StartedAt FinishedAt PreviousFinishedAt
----------- -------------------- --------- ---------- ------------------
1           Pending              20070101  20070103   NULL
1           Pending              20070104  20070105   20070103
1           Reviewed             20070105  20070114   20070105
1           Opened               20070114  20070119   20070114

(4 row(s) affected)
*/

Comments

# re: Using a Foreign key constraint to check validity of History Windows (Start - End Date Windows)

07 February 2008 22:20 by AdamMachanic

Clever!  I like it!

# re: Using a Foreign key constraint to check validity of History Windows (Start - End Date Windows)

18 December 2008 14:52 by Razvan Socol

We also need a constraint to check that StartedAt<=FinishedAt.

# Help with unique contraint | keyongtech

18 January 2009 16:52 by Help with unique contraint | keyongtech

Pingback from  Help with unique contraint | keyongtech