|
|
Question : How to maintain the dates of a history table aligned with no gaps?
|
|
|
|
Hi,
I have to mantain a history table, where given a new entry or a record is updated, the table is recreated or realigned (only for records with the same ID). There should be no gaps between the records. Each record has a StartDate and EndDate. Below is the schema of the table:
CREATE TABLE [dbo].[History] ( [HistoryID] bigint IDENTITY(1, 1) NOT NULL, [ID] int NOT NULL, [StartDate] datetime NOT NULL, [EndDate] datetime NULL, [Status] tinyint NULL, CONSTRAINT [History_pk] PRIMARY KEY CLUSTERED ([HistoryID]), CONSTRAINT [History_uq] UNIQUE ([ID], [StartDate]) ) ON [PRIMARY] GO
Here is a sample data with the dates aligned:
The DateFormat is dd/mm/yyyy
HistoryID ID StartDate EndDate Status 1 1 01/01/2010 10/01/2010 1 2 1 11/01/2010 15/01/2010 2 3 1 16/01/2010 20/01/2010 3
Rules: 1.Get rid of any rows that are entirely within the range of the new row being inserted or updated. example:
INSERT INTO History(ID,StartDate,EndDate,Status) VALUES(1,'01/01/2010','17/01/2010',2)
The end ResultSet should be as follows: HistoryID ID StartDate EndDate Status 6 1 01/01/2010 17/01/2010 2 3 1 18/01/2010 20/01/2010 3
Note that the record with HistoryID=3 have the StartDate increased by 1 because of the EndDate of the previous record(6) are greater than the StartDate of the record below(3), which range was from 16/01/2010 to 20/01/2010.
The same behavior should apply if the user makes an update on any record on any field (StartDate,EndDate,Status).
Following the previous resultset, lets INSERT more data into the table.
HistoryID ID StartDate EndDate Status 6 1 01/01/2010 17/01/2010 2 3 1 18/01/2010 20/01/2010 3
INSERT INTO History(ID,StartDate,EndDate,Status) VALUES(1,'05/01/2010','10/01/2010',1)
ResultSet after the insert:
HistoryID ID StartDate EndDate Status 6 1 01/01/2010 04/01/2010 2 7 1 05/01/2010 10/01/2010 1 8 1 11/01/2010 17/01/2010 2 3 1 18/01/2010 20/01/2010 3
Now the user changes the record with HistoryID=3, with the folowing UPDATE: UPDATE SET StartDate='17/01/2010', EndDate='19/01/2010', Status=1 WHERE HistoryID=3
Resultset:
HistoryID ID StartDate EndDate Status 6 1 01/01/2010 04/01/2010 2 7 1 05/01/2010 10/01/2010 1 8 1 11/01/2010 16/01/2010 2 3 1 17/01/2010 19/01/2010 1 9 1 20/01/2010 20/01/2010 3
Another INSERT:
INSERT INTO History(ID,StartDate,EndDate,Status) VALUES(1,'01/01/2010','18/01/2010',1)
Resultset:
HistoryID ID StartDate EndDate Status 10 1 01/01/2010 19/01/2010 1 9 1 20/01/2010 20/01/2010 3
Note that the records 7,8,3 were deleted. The new added record takes the EndDate modified to 19/01/2010 because of the EndDate of record no 3 had the same status, that means, that there is no need to keep or modify the record no 3 either, just take the EndDate of it and put it on the newly inserted record.
I dont know how to achive this result together with the behavior exposed here. I dont know if its better to do it as a trigger or to write a procedure for inserting records and do all the job in it.
That´s it. If there is any doubt about the rules, please, let me know.
Thanks!
Best Regards,
Marco André
|
|
|
|
Answer : How to maintain the dates of a history table aligned with no gaps?
|
|
|
|
hi Marco,
The huge number of records may introduce performance problems in both scenarios. The basic problem about performance is always the same: Test all scenarios and compare the results.
Finding your correct history record is simple:
SELECT TOP 1 * FROM [HistoryTable] WHERE [Date] <= [EventDate] ORDER BY [Date] DESC ;
You can use a user defined table-value function to calculate this record, but I cannot guess about its performance. Maybe there is a cool join, but I can't imagine one right now ;)
The multiple user problems are the same in both solutions, it is the same problem about atomicity as in the well-known bank account transfer problem. You need an explicit transaction control encapsulating your logic and an isolation level of read commit or better.
mfG --> stefan <--
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
|
SELECT *
FROM [EventTable] E
CROSS APPLY [dbo].[FindHistory](E.[ID], E.[EventDate]) H ;
-- with
CREATE FUNCTION [FindHistory]
(
@Id AS INT,
@EventDate AS DATETIME
)
RETURNS TABLE
AS
RETURN
( SELECT TOP 1
H.*
FROM [HistoryTable] H
WHERE H.ID = @ID
AND H.[HistoryDate] = @EventDate
ORDER BY H.[HistoryDate] DESC
)
|
|
|
|
|
|