Getting the row closest to time for each row in another table
Problem:
Hi I have a table with historical data from a sensor group and I am trying to get the row for each historical data record closest in time to the desired time. For example, I want the recording to be as close as possible to every minute.
I simplified the problem in the following way, which if I can solve, I can use to post my general solution:
Take two tables like this:
CREATE TABLE [TempDataTable](
[DataIndex] [int] IDENTITY(0,2) NOT NULL,
[DataName] [varchar](40) NOT NULL,
[DataValue] [decimal](10,2) NOT NULL,
[DataTimeStamp] [datetime2](7)
)
CREATE TABLE [TempTargetTable](
[TargetIndex] [int] IDENTITY(1,2) NOT NULL,
[TargetTime] [datetime2](7)
)
For each line in TempTargetTable
, get the line in TempDataTable
c TempDataTable.DataTimeStamp
closest toTempTargetTable.TargetTime
If I can do this, I'm sure I can figure out the rest, but I don't understand how to get this first step to work. For ease of testing the code, I can provide the following, which populates both tables with some test data:
Useful test data:
INSERT INTO [TempDataTable]
([DataName],
[DataValue],
[DataTimeStamp])
VALUES
('Sensor',0, '2017-01-01 00:00:00'),
('Sensor',0.5, '2017-01-01 00:00:17'),
('Sensor',1, '2017-01-01 00:01:03'),
('Sensor',1.5, '2017-01-01 00:01:30'),
('Sensor',1.5, '2017-01-01 00:01:38'),
('Sensor',2, '2017-01-01 00:02:01'),
('Sensor',2.5, '2017-01-01 00:02:15'),
('Sensor',3, '2017-01-01 00:02:56'),
('Sensor',3.5, '2017-01-01 00:03:27'),
('Sensor',4, '2017-01-01 00:04:01'),
('Sensor',5, '2017-01-01 00:05:00'),
('Sensor',5.5, '2017-01-01 00:05:15'),
('Sensor',5.5, '2017-01-01 00:05:46'),
('Sensor',6, '2017-01-01 00:06:10'),
('Sensor',7, '2017-01-01 00:06:57'),
('Sensor',7.5, '2017-01-01 00:07:13'),
('Sensor',8, '2017-01-01 00:08:01'),
('Sensor',9, '2017-01-01 00:09:03')
INSERT INTO [TempTargetTable]
([TargetTime])
VALUES
('2017-01-01 00:00:00'),
('2017-01-01 00:01:00'),
('2017-01-01 00:02:00'),
('2017-01-01 00:03:00'),
('2017-01-01 00:04:00'),
('2017-01-01 00:05:00'),
('2017-01-01 00:06:00'),
('2017-01-01 00:07:00'),
('2017-01-01 00:08:00'),
('2017-01-01 00:09:00')
source to share
For the current problem you are posting (simplified), I did the following:
Crosshair joined tables to make a difference for every target time, with every existing data timestamp. Then I applied a function DENSE_RANK
that will provide a ranking for each TargetTime, and then only those records with a minimum difference in milliseconds will be selected.
You can find a working solution here.
select TargetIndex, TargetTime, DataIndex, DataName, DataValue, DataTimeStamp
from
(
select t.*, DENSE_RANK() OVER(PARTITION BY t.targetindex ORDER BY t.diff) as Rank
from
(
select tg.targetindex, tg.targettime, t.dataindex, t.dataname, t.datavalue, t.datatimestamp, abs(datediff(ms, tg.TargetTime, t.DataTimeStamp)) diff
from TempDataTable t cross join TempTargetTable tg
) t
) f
where Rank = 1
source to share
if I read your question correctly, you want the closest entry even if it is in the previous minute. If so, you may receive this request. I did it in a few steps, so you can easily follow (hopefully)
What I've done:
- find the closest minute, if seconds> = 30 are rounded to the next minute, keep the actual minute
- calculate the difference in seconds and find the paragraph value
- get closest value to time point
Request
SELECT tempd.TargetTime, tdfinal.DataName, tdfinal.DataValue, tdfinal.DataTimeStamp
FROM @TempTargetTable as tempd
LEFT OUTER JOIN
(SELECT tdseconds.*, ROW_NUMBER() OVER(PARTITION BY closestMinute ORDER BY secondDiff) AS r
FROM (SELECT td.*, ABS(DATEDIFF(SECOND, DataTimeStamp, closestMinute)) AS secondDiff
FROM (SELECT DataName,DataValue,DataTimeStamp,
CONVERT(DATETIME,CONVERT(DATE, datatimestamp, 121)) +
CONVERT (DATETIME,TIMEFROMPARTS(DATEPART(HOUR, datatimestamp),
CASE WHEN DATEPART(SECOND, DataTimeStamp) >= 30
THEN DATEPART(MINUTE, DATATimeStamp) + 1
ELSE DATEPART(MINUTE, DATATimeStamp) END, 0,0,0), 121) AS closestMinute
FROM @TempDataTable ) AS td
) AS tdseconds
) AS tdfinal
ON tdfinal.closestMinute = tempd.TargetTime
WHERE tdfinal.r = 1
Result
TargetTime DataName DataValue DataTimeStamp
2017-01-01 00:00:00.000 Sensor 0.00 2017-01-01 00:00:00.000
2017-01-01 00:01:00.000 Sensor 1.00 2017-01-01 00:01:03.000
2017-01-01 00:02:00.000 Sensor 2.00 2017-01-01 00:02:01.000
2017-01-01 00:03:00.000 Sensor 3.00 2017-01-01 00:02:56.000
2017-01-01 00:04:00.000 Sensor 4.00 2017-01-01 00:04:01.000
2017-01-01 00:05:00.000 Sensor 5.00 2017-01-01 00:05:00.000
2017-01-01 00:06:00.000 Sensor 6.00 2017-01-01 00:06:10.000
2017-01-01 00:07:00.000 Sensor 7.00 2017-01-01 00:06:57.000
2017-01-01 00:08:00.000 Sensor 8.00 2017-01-01 00:08:01.000
2017-01-01 00:09:00.000 Sensor 9.00 2017-01-01 00:09:03.000
source to share