Reputation: 884
I am trying to clean up a not so useful history table by changing it's format. For the usage of the history table it is relevant between which time a row was valid.
The current situation:
Unit | Value | HistoryOn |
----------------------------------------
1 | 123 | 2013-01-05 14:16:00
1 | 234 | 2013-01-07 12:12:00
2 | 325 | 2013-01-04 14:12:00
1 | 657 | 2013-02-04 17:11:00
3 | 132 | 2013-04-02 13:00:00
The problem that arises here is that as this table grows it will become increasingly resource hungry when I want to know what status all of my containers had during a certain period. (say I want to know the value for all units on a specific date)
My solution is to create a table in this format:
Unit | value | HistoryStart | HistoryEnd |
---------------------------------------------------------------------
1 | 123 | 2013-01-05 14:16:00 | 2013-01-07 12:11:59
1 | 234 | 2013-01-07 12:12:00 | 2013-02-04 17:10:59
1 | 657 | 2013-02-04 17:11:00 | NULL
2 | 325 | 2013-01-04 14:12:00 | NULL
3 | 132 | 2013-04-02 13:00:00 | NULL
Note that the NULL value in HistoryEnd here indicates that the row is still representative of the current status.
I have tried to make use of a left join on the table itself using the HistoryOn field. This had the unfortunate side effect of cascading in an undesired manner.
SQL Query used:
SELECT *
FROM webhistory.Units u1 LEFT JOIN webhistory.Units u2 on u1.Unit = u2.Unit
AND u1.HistoryOn < u2.HistoryOn
WHERE u1.Units = 1
The result of the query is as follows:
Unit | Value | HistoryOn | Unit | Value | HistoryOn |
-------------------------------------------------------------------------------------
1 | 657 | 2013-02-04 17:11:00 | NULL | NULL | NULL
1 | 234 | 2013-01-07 12:12:00 | 1 | 657 | 2013-02-04 17:11:00
1 | 123 | 2013-01-05 14:16:00 | 1 | 657 | 2013-02-04 17:11:00
1 | 123 | 2013-01-05 14:16:00 | 1 | 234 | 2013-01-07 12:12:00
This effect is incremental because each entry will join on all the entries that are newer than itself instead of only the first entry that comes after it.
Sadly right as of yet I am unable to come up with a good query to solve this and would like insights or suggestions that could help me solve this migration problem.
Upvotes: 0
Views: 74
Reputation: 6477
First data sample
create table Data(
Unit int,
Value int,
HistoryOn datetime)
insert into Data
select 1,123,'2013-01-05 14:16:00'
union select 1 , 234 , '2013-01-07 12:12:00'
union select 2 , 325 , '2013-01-04 14:12:00'
union select 1 , 657 , '2013-02-04 17:11:00'
union select 3 , 132 , '2013-04-02 13:00:00'
I created a function to calculate HistoryEnd Noticed I named Data to table
CREATE FUNCTION dbo.fnHistoryEnd
(
@Unit as int,
@HistoryOn as datetime
)
RETURNS datetime
AS
BEGIN
-- Declare the return variable here
DECLARE @HistoryEnd as datetime
select top 1 @HistoryEnd=dateadd(s,-1,d.HistoryOn )
from Data d
where d.HistoryOn>@HistoryOn and d.Unit=@Unit
order by d.HistoryOn asc
RETURN @HistoryEnd
END
GO
Then, the query is trivial
select *,dbo.fnHistoryEnd(a.Unit,a.HistoryOn) from Data a
order by Unit, HistoryOn
EDIT
Don't forget order by clause in sub query. Look what could happen if not
CREATE TABLE #webhist(
Unit int,
Value int,
HistoryOn datetime
)
INSERT INTO #webhist VALUES
(1, 234, '2013-01-07 12:12:00'),
(2, 325, '2013-01-04 14:12:00'),
(1, 657, '2013-02-04 17:11:00'),
(3, 132, '2013-04-02 13:00:00'),
(1, 123, '2013-01-05 14:16:00')
select *, (select top 1 historyon from #webhist u2 where u2.historyon > u1.historyon and u1.unit = u2.unit) from #webhist u1;
select *, (select top 1 historyon from #webhist u2 where u2.historyon > u1.historyon and u1.unit = u2.unit order by u2.HistoryOn) from #webhist u1;
drop table #webhist
Upvotes: 1
Reputation: 121
Maybe I'm missing something, but this seems to work:
CREATE TABLE #webhist(
Unit int,
Value int,
HistoryOn datetime
)
INSERT INTO #webhist VALUES
(1, 123, '2013-01-05 14:16:00'),
(1, 234, '2013-01-07 12:12:00'),
(2, 325, '2013-01-04 14:12:00'),
(1, 657, '2013-02-04 17:11:00'),
(3, 132, '2013-04-02 13:00:00')
SELECT
u1.Unit
,u1.Value
,u1.HistoryOn AS HistoryStart
,u2.HistoryOn AS HistoryEnd
FROM #webhist u1
OUTER APPLY (
SELECT TOP 1 *
FROM #webhist u2
WHERE u1.Unit = u2.Unit AND u1.HistoryOn < u2.HistoryOn
ORDER BY HistoryOn
) u2
DROP TABLE #webhist
Upvotes: 1