【问题标题】:Find missing time intervals in a table在表格中查找缺失的时间间隔
【发布时间】:2012-06-06 06:13:28
【问题描述】:

我有下表,其中包含每 15 分钟从几个不同设备读取的值:

ID   DeviceID   Date                    Value
----------------------------------------------
1    3          24.08.2011 00:00:00     0.51
2    3          24.08.2011 00:15:00     2.9
3    3          24.08.2011 00:30:00     0
4    3          24.08.2011 00:45:00     7.1
5    3          24.08.2011 01:00:00     1.05
6    3          24.08.2011 03:15:00     3.8

我想在表格中找出给定月份的每个设备的所有空白,其中没有条目。对于上表,结果应该是这样的:

DeviceID    StartDate               EndDate
-------------------------------------------------------
3           24.08.2011 01:00:00     24.08.2011 03:15:00

该表大约有 35000 个设备和 1 亿个条目。

这是我尝试过的;它相当慢,但返回了我需要的东西。然而,除了它的速度之外,还有另一个问题:它只查找到设备在给定月份的最后一个条目的缺失间隔;之后的任何内容都将被忽略,因此可能会错过额外的缺失值间隔。

SELECT
    t2.Date AS StartDate
    , t1.Date AS EndDate
FROM
    TestTable t1
    INNER JOIN TestTable t2 ON t1.DeviceID = t2.DeviceID
WHERE
    (t2.Date = (SELECT MAX(Date) FROM TestTable t3 WHERE t3.Date < t1.Date AND t3.DeviceID = t1.DeviceID)
        AND DATEDIFF(MINUTE, t2.Date, t1.Date) > 15)
    AND t1.DeviceID = @id
    AND DATEPART(YEAR, t1.Date) = @year AND DATEPART(MONTH, t1.Date) = @month

【问题讨论】:

  • 你可能想看看 at this 它的示例章节,涵盖了 Itzik Ben-Gan 中的间隙和岛屿主题
  • @ConradFrix 感谢您的链接。
  • @NikolaMarkovinović 谢谢,但我自己删除了这个问题。这是一个糟糕的问题,我自己设法弄清楚了(我也使用了ROW_NUMBER())。

标签: sql sql-server-2008 gaps-and-islands


【解决方案1】:

以下应该可以工作,并且不会只返回一个 deviceid 的记录。

本文的要点是

  • 为每条记录添加一个行号,按Date 排序并为每个DeviceID 重新启动。
  • 与 self 连接以创建包含两个原始行组合的行的结果。每行的列之间的关系是行号(+1)和DeviceID
  • 仅保留相关 Date 超过 15 分钟的行。

SQL 语句

;WITH t AS (
  SELECT  *, rn = ROW_NUMBER() OVER (PARTITION BY DeviceID ORDER BY Date)
  FROM    TestTable
)  
SELECT  t1.DeviceID, t1.Date, t2.Date
FROM    t t1
        INNER JOIN t t2 ON t2.DeviceID = t1.DeviceID AND t2.rn = t1.rn + 1
WHERE   DATEDIFF(MINUTE, t1.Date, t2.Date) > 15        

测试脚本

;WITH TestTable (ID, DeviceID, Date, Value) AS (
  SELECT 1, 3, '2011-08-24 00:00:00', 0.51 UNION ALL
  SELECT 2, 3, '2011-08-24 00:15:00', 2.9 UNION ALL
  SELECT 3, 3, '2011-08-24 00:30:00', 0 UNION ALL
  SELECT 4, 3, '2011-08-24 00:45:00', 7.1 UNION ALL
  SELECT 5, 3, '2011-08-24 01:00:00', 1.05 UNION ALL
  SELECT 6, 3, '2011-08-24 03:15:00', 3.8 
)
, t AS (
  SELECT  *, rn = ROW_NUMBER() OVER (PARTITION BY DeviceID ORDER BY Date)
  FROM    TestTable
)  
SELECT  t1.DeviceID, t1.Date, t2.Date
FROM    t t1
        INNER JOIN t t2 ON t2.DeviceID = t1.DeviceID AND t2.rn = t1.rn + 1
WHERE   DATEDIFF(MINUTE, t1.Date, t2.Date) > 15        

【讨论】:

  • 你删除了参数@id@year@month
  • @ConradFrix - 我错过了,但肯定 OP 知道如何将适当的值添加到 where 子句。
  • 奇怪的是,它不会返回任何关于我拥有的数据的内容。我会再调查一下,看看发生了什么。
  • @alex - 如果你发现了,请告诉我们...waiting>
  • @Lieven 抱歉,我一开始一定是搞错了。它有效,与我所做的相比,它的速度惊人地快。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-30
  • 1970-01-01
  • 1970-01-01
  • 2017-05-06
  • 1970-01-01
  • 2015-05-01
  • 1970-01-01
相关资源
最近更新 更多