查找间隔之间的缺失数据

问题描述 投票:0回答:4

我有一个系统,每小时在同一分钟内插入寄存器,例如:

DateTime              Value
2023/05/01 06:14        10
2023/05/01 06:29        15
2023/05/01 06:44        21
2023/05/01 06:59        12

所以我想做一个查询,返回给定范围内可能丢失的记录。

DateTime              Value
2023/05/01 06:14        10
2023/05/01 06:29        15
2023/05/01 06:44        21
2023/05/01 06:59        12
2023/05/01 07:29        10
2023/05/01 07:44        17
2023/05/01 08:14        20

在此记录中,我希望查询返回我:

DateTime
2023/05/01 07:14
2023/05/01 07:59

我读过一些类似问题的帖子:Find Missing Dates in Data

但我找不到解决方案。

有什么想法吗?非常感谢!

sql sql-server missing-data
4个回答
0
投票

你必须根据分钟设置间隔(我设置为15分钟)

我根据你的数据得到了开始日期和结束日期,你也可以设置

DECLARE @StartDate DATETIME 
DECLARE @EndDate DATETIME
DECLARE @interval int=15

SET @StartDate =(select min(_DateTime) from #test)
SET @EndDate = (select max(_DateTime) from #test)

;WITH Dates(Date) AS
(
    SELECT DATEADD(MINUTE, @interval, @StartDate) AS Date
    UNION ALL
    SELECT DATEADD(MINUTE, @interval, Date) AS Date
    FROM Dates
    WHERE Date < @EndDate
)
SELECT a.Date
FROM  Dates a
left join #test b on a.Date=b._DateTime
where b._DateTime is null
option (maxrecursion 0)

结果: |日期| |--| |2023-05-01 07:14:00.000| |2023-05-01 07:59:00.000|

基础数据:

create table #test(_DateTime DateTime,_value int)
insert into #test(_DateTime,_value) values('2023/05/01 06:14',        10)
insert into #test(_DateTime,_value) values('2023/05/01 06:29',        15)
insert into #test(_DateTime,_value) values('2023/05/01 06:44',        21)
insert into #test(_DateTime,_value) values('2023/05/01 06:59',        12)
insert into #test(_DateTime,_value) values('2023/05/01 07:29',        10)
insert into #test(_DateTime,_value) values('2023/05/01 07:44',        17)
insert into #test(_DateTime,_value) values('2023/05/01 08:14',        20)

0
投票

这是一个解决方案。我假设两天之间的时差不超过 24 小时,如果不是这样,您需要增加 CTE 值:

DECLARE @times TABLE (
    datetime datetime, value int)
INSERT INTO @times
SELECT  datetime, value
FROM    (
        VALUES  ('2023/05/01 06:14',10)
        ,   ('2023/05/01 06:29',15)
        ,   ('2023/05/01 06:44',21)
        ,   ('2023/05/01 06:59',12)
        ,   ('2023/05/01 07:29',10)
        ,   ('2023/05/01 07:44',17)
        ,   ('2023/05/01 08:14',20)
        ,   ('2023/05/01 18:14',20)
    ) t (datetime, value)
    
;WITH cte AS (
    SELECT  ROW_NUMBER() OVER(ORDER BY @@spid) AS row
    FROM    (VALUES(1),(1),(1),(1)) x(v)
    CROSS apply (VALUES(1),(1),(1),(1),(1),(1)) y(y)
)
SELECT  prevDate, missingDate, datetime
FROM    (
    SELECT  DATEDIFF(HOUR, LAG(datetime) OVER(ORDER BY datetime), datetime) AS diff
    ,   LAG(datetime) OVER(ORDER BY datetime) AS prevDate
    ,   *
    FROM    @times
    ) z
CROSS APPLY (
    SELECT  missingDate
    ,   c.*
    FROM    cte c
    CROSS apply (VALUES(14),(29),(44),(59)) m(m)
    CROSS apply (
        SELECT  DATEADD(hour, c.row-1, DATEADD(minute, m.m, DATEADD(MINUTE, -DATEPART(MINUTE, z.prevDate), z.prevDate))) AS missingDate
        ) sd
    WHERE   c.row BETWEEN 1 AND diff + 1
    AND missingDate > prevDate 
    AND missingDate < datetime
    ) q
ORDER BY datetime

这样做是获取每个日期时间与其先前值之间的差距,然后在一系列 CTE 的帮助下生成这两天之间缺失时间的列表,以及固定时间列表 ((14),(29) ,(44),(59)).

DATEADD(hour, c.row-1, DATEADD(minute, m.m, DATEADD(MINUTE, -DATEPART(MINUTE, z.prevDate), z.prevDate)))
这部分通过从前一个日期中删除分钟来生成缺失的日期,然后在两个日期之间添加小时和固定分钟。


0
投票

根据我的理解,每个条目之间应该有 15 分钟的时间间隔。即 07:14-07:29 和 07:59-08:14 的时间范围就是这种情况,其中 07:14 和 07:59 被指定为缺失的时间范围。

实现这一点的有效方法是首先识别每个后续时间戳之间的时间间隔差异。这可以在子查询中使用 LEAD 函数来完成。

为了便于说明,让我们考虑以下示例。假设有一列日期如下:

现在,让我们假设一个人希望在存在大于 15 分钟的时间间隔的日期上增加 15 分钟。

这可以通过以下方式完成:

SELECT date, next_date, next_date - date as difference, date + interval '15' minute from (select date, LEAD(date, 1) 
OVER (ORDER BY date) 
AS next_date from table) as subquery where next_date-date>'15 minutes';

在此表中,最后一列在原始时间戳上增加了 15 分钟——但不显示那些间隔为 15 分钟或更短的时间戳。

因此,可以选择仅显示此列以显示所需的时间戳原始时间戳 + 15 分钟,但仅限于时间戳之间的差异大于 15 分钟。


0
投票

生成所有可能的时间,然后对您的表进行左连接:

DECLARE @start_time DATETIME = '2023-05-01 06:14';
DECLARE @end_time DATETIME = '2023-05-01 08:14'
DECLARE @interval_in_sec INT = 60*15;

WITH all_times AS -- using row_number from the [master]..spt_values table to generate all possible times.
(
  SELECT TOP (DATEDIFF(SECOND, @start_time, @end_time) / @interval_in_sec)  
     DATEADD(SECOND, ROW_NUMBER() OVER (ORDER BY number) * @interval_in_sec,  @start_time) AS dt
  FROM [master]..spt_values -- system tables can be changed by the vendor without pre-warning, it would be better to create your own numbers table
)
SELECT TS.dt AS Date_Time
FROM all_times TS LEFT JOIN table_name TB
ON TS.dt = TB.Date_Time
WHERE TB.Date_Time IS NULL
ORDER BY TS.dt

在SQL Server 2022中,我们可以使用

GENERATE_SERIES
函数:

WITH all_times AS
(
  SELECT 
     DATEADD(SECOND, value * @interval_in_sec,  @start_time) AS dt
  FROM GENERATE_SERIES(0,DATEDIFF(SECOND, @start_time, @end_time) / @interval_in_sec,1)
)
...

演示

© www.soinside.com 2019 - 2024. All rights reserved.