【问题标题】:SQL select closest row to full hourSQL选择最接近整小时的行
【发布时间】:2016-12-16 14:17:43
【问题描述】:

这是我的 SQL Server 2012 表(没有固定的插入间隔):

DateTime                    Value
2016-12-16 15:08:03.0740000 17,11233139
2016-12-16 15:08:02.0560000 17,8571434
2016-12-16 15:08:00.0410000 17,11233139
2016-12-16 15:07:58.6570000 17,93345451
2016-12-16 15:07:54.9970000 17,11538506
2016-12-16 15:07:53.5910000 17,93345451
2016-12-16 15:06:45.3220000 17,93650818
2016-12-16 15:06:44.1230000 18,65079308
2016-12-16 15:01:09.0470000 20,41208839
2016-12-16 15:01:05.4060000 19,58791161
2016-12-16 15:01:03.3970000 20,41208839
2016-12-16 15:01:00.4070000 19,73138046
2016-12-16 15:00:57.2230000 20,41208839
2016-12-16 15:00:21.0380000 20,51892471
2016-12-16 15:00:19.0270000 21,22100067
2016-12-16 14:42:45.1810000 20,40903473
2016-12-16 14:27:40.0050000 19,59401703
2016-12-16 14:20:40.2510000 18,65995026
2016-12-16 14:19:03.7750000 18,65995026
2016-12-16 14:01:55.0120000 17,93955994
2016-12-16 13:59:07.9490000 17,12454224
2016-12-16 13:59:06.1180000 16,39499474

我希望每个小时只有一排,最接近整点的时间。例如。对于第 14 小时,这是最接近整小时的值:

2016-12-16 14:01:55.0120000 17,93955994
2016-12-16 13:59:07.9490000 17,12454224

第 2 行(53 秒)与 14:00:00 的差异较小,因此应采用这一行。

我怎么能这样做?谢谢

【问题讨论】:

  • “每小时”是什么意思?一天有24小时。那么对于每个小时你想要最接近的记录?即使有很大的差距?与您的数据给出的最接近晚上 7 点的记录将是 15:08:03 记录。你想要这种方式还是只需要几个小时?

标签: sql sql-server


【解决方案1】:

使用min窗口函数。

select datetimecol,value 
from (
select t.*,min(datetimecol) over(partition by cast(datetimecol as date),datepart(hour,datetimecol)) mintmstmp
from tablename t
) x
where datetimecol=mintmstmp

编辑 1:要在给定日期获取最接近特定时间的值,请使用

select top 1 datetimecol,val 
from (select t.*,
      abs(datediff(second,'2016-12-16 14:00:00.0000000',datetimecol)) df
      from tablename t
      ) x
order by df

编辑 2:一种选择是使用递归 cte 生成给定一天的所有小时,并将其连接到现有表以根据时差获取最接近的时间戳。

with datetimes as (select cast('2016-12-16 00:00:00.0000000' as datetime2) dt
                   union all
                   select dateadd(hour,1,dt) from datetimes where dt < '2016-12-17 00:00:00.0000000')
select datetimecol,val,dt closest_to_hour
from (
select t.*,dt,
row_number() over(partition by dt order by abs(datediff(second,d.dt,datetimecol))) rn
from tablename t 
join datetimes d on datepart(hour,d.dt) between datepart(hour,datetimecol) and datepart(hour,datetimecol)+1
and cast(d.dt as date) = cast(t.datetimecol as date)
--change this join condition per your specifications
) x
where rn = 1

Sample Demo

【讨论】:

  • 分区上的字段不应该倒序吗?
  • 这只需要整个小时的最小值。但是如果前一小时的日期更接近呢?就像在 OP 的例子中一样。
  • 那不是只能抓取同一小时的时间吗?例如14.00 将选择 14.05 但错过 13.57
  • 你的第二个答案只返回一行。
  • “我希望每个小时只有一行,其中最接近整小时的时间”因此需要多个小时。它只是发生示例数据是一个小时。
【解决方案2】:
WITH CTE_ClosestToTheHour AS (

SELECT DateTime, Value,

ROW_NUMBER() OVER (PARTITION BY DATEADD(HH, 
                                DATEPART(HH, DateTime),
                                    CAST(CAST(DateTime AS DATE) AS DATETIME)
                                    )

                    ORDER BY 
                            ABS(
                            DATEDIFF(ms, DateTime, 

                                DATEADD(HH, 
                                DATEPART(HH, DateTime),
                                    CAST(CAST(DateTime AS DATE) AS DATETIME)
                                    )
                                    )
                                    )
                            ASC
                    ) AS RN

FROM table
)

SELECT * 
FROM CTE_ClosestToTheHour
WHERE RN = 1
ORDER BY DateTime

【讨论】:

  • 这给你的结果和我的回答一样。
  • 不,它没有。
  • 我建议您验证它,而不是仓促否决答案。
  • 嗯,您的第一个答案是错误的,而您的第二个答案仅适用于一天中的一个小时。做对了,我会投赞成票。
  • @GarethLyons 谢谢,但它给了我一个错误Msg 156, Level 15, State 1, Line 27 Incorrect syntax near the keyword 'SELECT'. 我认为WITH CTE_ClosestToTheHour AS ( 缺少)
【解决方案3】:

这利用DATEADD 将日期添加 30 分钟,然后截断到最接近的小时。 ROW_NUMBER 函数用于在此日期进行分区,按此日期与实际日期时间之间的差异排序,然后仅选择每个分区的第一行:

;WITH CTE
AS
(
    SELECT dt, val,
            ROW_NUMBER() OVER 
             (PARTITION BY dateadd(hour, 
                   datediff(hour, 0, dateadd(mi, 30, dt)), 0) 
             ORDER BY 
                ABS(DateDiff(ms, dt,dateadd(hour, 
                  datediff(hour, 0, dateadd(mi, 30, dt)), 0)))) AS RN
    FROM #T
)
SELECT *
FROM CTE
WHERE RN=1

【讨论】:

  • 谢谢,但这只会返回当前小时的最接近的值,而不是过去的值
猜你喜欢
  • 2020-05-07
  • 1970-01-01
  • 1970-01-01
  • 2018-06-12
  • 2012-07-11
  • 2021-02-02
  • 1970-01-01
  • 2018-01-07
  • 2015-06-21
相关资源
最近更新 更多