每30分钟选择最接近的时间戳

时间:2017-03-13 11:35:36

标签: sql tsql

假设我在表格中有以下时间戳数据:

Id                   Timestamp
-------------------- -----------------------
1                    2016-09-19 13:17:24.000
2576                 2016-09-19 13:47:24.000
4945                 2016-09-19 14:17:24.000
7538                 2016-09-19 14:47:24.000
10016                2016-09-19 15:17:24.000
10570                2016-09-19 15:24:51.000
11968                2016-09-19 15:47:55.000
11990                2016-09-19 15:48:08.000
13648                2016-09-19 16:18:08.000
14742                2016-09-19 16:36:55.000

现在我希望从start_date中选择最接近的时间戳,并搜索找到的时间戳+30分钟的下一个最接近的时间戳

简短的例子:

start_date = 2016-09-19 13:00:00.000
end_date = 2016-09-19 16:00:00.000
Now it should find record:
2016-09-19 13:17:24.000
Now we add 30 minutes to found date, so we will search closest timestamp for 13:47:23.000 and so on until date = 16:00.

注意:它应该是最接近的近似值,因此它可以小于30分钟差异

完整示例:

start_date = 2016-09-19 13:00:00.000
end_date = 2016-09-19 16:00:00.000

Id                   Timestamp
-------------------- -----------------------
1                    2016-09-19 13:17:24.000
2576                 2016-09-19 13:47:24.000
4945                 2016-09-19 14:17:24.000
7538                 2016-09-19 14:47:24.000
10016                2016-09-19 15:17:24.000
11968                2016-09-19 15:47:55.000

我怎么能做到这一点?我宁愿避免使用光标,也可以通过

来完成

3 个答案:

答案 0 :(得分:2)

如果IdTimestamp出现在同一订单中,请使用以下代码(不是递归或CTE):

SELECT *
INTO #TempTable
FROM (VALUES
    (1,     CAST('2016-09-19 13:17:24.000' AS DATETIME)),
    (2576,  CAST('2016-09-19 13:47:24.000' AS DATETIME)),
    (4945,  CAST('2016-09-19 14:17:24.000' AS DATETIME)),
    (7538,  CAST('2016-09-19 14:47:24.000' AS DATETIME)),
    (10016, CAST('2016-09-19 15:17:24.000' AS DATETIME)),
    (10570, CAST('2016-09-19 15:24:51.000' AS DATETIME)),
    (11968, CAST('2016-09-19 15:47:55.000' AS DATETIME)),
    (11990, CAST('2016-09-19 15:48:08.000' AS DATETIME)),
    (13648, CAST('2016-09-19 16:18:08.000' AS DATETIME)),
    (14742, CAST('2016-09-19 16:36:55.000' AS DATETIME)))
    AS T (Id, [Timestamp])

DECLARE
    @StartDate DATETIME = '2016-09-19 13:00:00.000',
    @EndDate DATETIME = '2016-09-19 16:00:00.000';

SELECT MIN(Id) AS Id, MIN([Timestamp]) AS [Timestamp]
FROM #TempTable
WHERE [Timestamp] BETWEEN @StartDate AND @EndDate
GROUP BY
    CAST([Timestamp] AS DATE), -- day
    DATEPART(hour, [Timestamp]), -- hour
    DATEPART(minute, [Timestamp])/30 -- half an hour (0 or 1)
ORDER BY Id

如果我们没有这样的排序并且多次出现相同的Timestamp,则可以使用CTE:

DECLARE
    @StartDate DATETIME = '2016-09-19 13:00:00.000',
    @EndDate DATETIME = '2016-09-19 16:00:00.000';

WITH TargetTimestamps AS
(
    SELECT MIN([Timestamp]) AS MinTimestamp
    FROM #TempTable
    WHERE [Timestamp] BETWEEN @StartDate AND @EndDate
    GROUP BY
        CAST([Timestamp] AS DATE), -- day
        DATEPART(hour, [Timestamp]), -- hour
        DATEPART(minute, [Timestamp])/30 -- half an hour (0 or 1)
)
SELECT MIN(Id) AS Id, MinTimestamp
FROM #TempTable
JOIN TargetTimestamps ON [Timestamp] = MinTimestamp
GROUP BY MinTimestamp -- use grouping to avoid duplicates for the same [Timestamp]
ORDER BY MinTimestamp

答案 1 :(得分:0)

您可以在单个查询中执行此操作。我不能100%确定你真正需要的是什么(你的例子似乎是在收缩声明)。但这是一般结构:

with x as (
      select min(timestamp) as first_timestamp
      from t
      where timestamp >= @start_date
     )
select t.*
from t join
     x
     on t.timestamp >= x.first_timestamp and
        t.timestamp < dateadd(minute, 30, x.first_timestamp);

答案 2 :(得分:0)

递归由

决定
  

找到时间戳的下一个最接近的时间戳+ 30分钟

不幸的是,CTE不允许在递归部分使用TOP(1)。解决方法是使用row_number()... = 1

SELECT *
INTO #TempTable
FROM (VALUES
    (1,     CAST('2016-09-19 13:17:24.000' AS DATETIME)),
    (2576,  CAST('2016-09-19 13:47:24.000' AS DATETIME)),
    (4945,  CAST('2016-09-19 14:17:24.000' AS DATETIME)),
    (7538,  CAST('2016-09-19 14:47:24.000' AS DATETIME)),
    (10016, CAST('2016-09-19 15:17:24.000' AS DATETIME)),
    (10570, CAST('2016-09-19 15:24:51.000' AS DATETIME)),
    (11968, CAST('2016-09-19 15:47:55.000' AS DATETIME)),
    (11990, CAST('2016-09-19 15:48:08.000' AS DATETIME)),
    (13648, CAST('2016-09-19 16:18:08.000' AS DATETIME)),
    (14742, CAST('2016-09-19 16:36:55.000' AS DATETIME)))
    AS T (Id, [Timestamp])

DECLARE
    @StartDate DATETIME = '2016-09-19 13:00:00.000',
    @EndDate DATETIME = '2016-09-19 16:00:00.000';


with found as (
    select top(1) *, r = 1
    from #TempTable
    where [Timestamp] between @StartDate and @EndDate
    order by [Timestamp]
    union all
    select t.*, r = cast(row_number() over (partition by f.r order by t.[Timestamp] ) as int)
    from found f
    join #TempTable t on t.[Timestamp] >= dateadd(minute, 30, f.[Timestamp])
        and t.[Timestamp] between @StartDate and @EndDate
        and f.r=1 
)
select *
from found
where r = 1
order by [Timestamp]