如何使用SQL查找丢失的数据行?

时间:2009-12-05 14:08:03

标签: mysql database

我的问题

我有一个MySQL数据库,按时间顺序存储大量的气象数据(每10分钟插入一次新数据)。不幸的是,有几次停电,因此缺少某些行。我最近设法从气象站获取了某些备份文件,现在我想使用它们填写缺失的数据。

DB ist结构如下(示例):

date*            the data    
2/10/2009 10:00  ...
2/10/2009 10:10  ...
( Missing data!)
2/10/2009 10:40  ...
2/10/2009 10:50  ...
2/10/2009 11:00  ...
...

* = datatime - 类型,主键

我的想法

由于备份和数据库位于不同的计算机上并且流量非常慢,我想到了创建一个MySQL查询,该查询在运行时将返回指定范围内所有缺失日期的列表时间然后,我可以从备份中提取这些日期并将它们插入数据库。

问题

如何编写这样的查询?我没有权限创建任何辅助表。是否可以在指定的时间间隔内制定所有必需日期的“虚拟表”,然后在JOIN中使用它?或者是否有完全不同的命题来解决我的问题?

修改: 是的,时间戳始终采用上面显示的形式(总是10分钟),除了一些只是缺失。

好的,临时表怎么样?是否有一种优雅的方式自动填充时间范围?如果两个脚本试图同时运行,这会导致表格出现问题吗?

7 个答案:

答案 0 :(得分:5)

select t1.ts as hival, t2.ts as loval
from metdata t1, metdata t2
where t2.ts = (select max(ts) from metdata t3
where t3.ts < t1.ts)
and not timediff(t1.ts, t2.ts) = '00:10:00'

此查询将返回可用于选择缺失数据的对联。对于查询返回的每个对联,丢失的数据将具有hival和loval之间的时间戳。

编辑 - thx进行检查,Craig

EDIT2:

获取缺少的时间戳 - 这个SQL有点难以阅读,所以我会稍微分解一下。首先,我们需要一种方法来计算给定的低值和高值之间的一系列时间戳值,间隔为10分钟。无法创建表时执行此操作的方法基于以下sql,它将结果集创建为0到9之间的所有数字。

select d1.* from 
(select 1 as digit
union select 2 
union select 3 
union select 4 
union select 5 
union select 6 
union select 7 
union select 8 
union select 9 
union select 0 
) as d1

...现在通过将此表与其自身的副本组合几次意味着我们可以动态生成指定长度的列表

select curdate() + 
INTERVAL  (d1.digit * 100 + d2.digit * 10 + d3.digit) * 10 MINUTE 
as date 
from (select 1 as digit
union select 2 
union select 3 
union select 4 
union select 5 
union select 6 
union select 7 
union select 8 
union select 9 
union select 0 
) as d1
join
(select 1 as digit
union select 2 
union select 3 
union select 4 
union select 5 
union select 6 
union select 7 
union select 8 
union select 9 
union select 0 
) as d2
join
(select 1 as digit
union select 2 
union select 3 
union select 4 
union select 5 
union select 6 
union select 7 
union select 8 
union select 9 
union select 0 
) as d3
where (d1.digit * 100 + d2.digit * 10 + d3.digit) between 1 and 42
order by 1

...现在这段sql已经接近我们需要的了。它有2个输入变量:

  1. 一个开始时间戳(我用过 示例中的curdate());和
  2. 迭代次数 - 在哪里     子句指定42次迭代     例如,最多3个数字表是1000个间隔
  3. ...这意味着我们可以使用原始的sql从上面驱动示例,为每个hival lowval对生成一系列时间戳。忍受我,这个sql现在有点长了......

    select daterange.loval + INTERVAL  (d1.digit * 100 + d2.digit * 10 + d3.digit) * 10 MINUTE as date 
    from 
    (select t1.ts as hival, t2.ts as loval
    from metdata t1, metdata t2
    where t2.ts = (select max(ts) from metdata t3
    where t3.ts < t1.ts)
    and not timediff(t1.ts, t2.ts) = '00:10:00'
    ) as daterange
    join
    (select 1 as digit
    union select 2 
    union select 3 
    union select 4 
    union select 5 
    union select 6 
    union select 7 
    union select 8 
    union select 9 
    union select 0 
    ) as d1
    join
    (select 1 as digit
    union select 2 
    union select 3 
    union select 4 
    union select 5 
    union select 6 
    union select 7 
    union select 8 
    union select 9 
    union select 0 
    ) as d2
    join
    (select 1 as digit
    union select 2 
    union select 3 
    union select 4 
    union select 5 
    union select 6 
    union select 7 
    union select 8 
    union select 9 
    union select 0 
    ) as d3
    where (d1.digit * 100 + d2.digit * 10 + d3.digit) between 1 and
     round((time_to_sec(timediff(hival, loval))-600) /600)
    order by 1
    

    ...现在有一些史诗般的sql
    注意:使用数字表3次会给出一个超过6天的最大间隙

答案 1 :(得分:1)

如果您可以创建临时表,可以使用JOIN解决问题

CREATE TEMPORARY TABLE DateRange
(theDate DATE);

在日期之间间隔10分钟填充表格,然后使用以下

SELECT theDate
FROM DateRange dr
LEFT JOIN Meteorological mm on mm.date = dr.theDate
WHERE mm.date IS NULL

结果将是您的天气表中没有条目的所有日期/时间。

如果您需要快速查找缺少数据的日期,可以使用

select Date(mm.Date),144-count(*) as TotMissing 
from Meteorological mm 
group by Date(mm.Date) 
having count(*) < 144 

假设每天24小时,每小时6个条目(因此144行)。 - Sparky 0秒前

答案 2 :(得分:0)

创建临时表(JOIN)。或者采取所有日期并在本地查询它们,你应该有自由统治(循环/哈希)。

对于JOIN,您生成的所有日期的引用都是您的基表,您的数据是您的联接表。寻找不存在连接数据的对,并选择生成的日期。

答案 3 :(得分:0)

作为使用 Sql Server 的快速解决方案,请检查没有日期+间隔追随者的日期。我认为MySql确实有某种dateadd函数,但你可以试试这样的东西。这将显示缺少数据的范围。

DECLARE @TABLE TABLE(
        DateValue DATETIME
)

INSERT INTO @TABLE SELECT '10 Feb 2009 10:00:00'
INSERT INTO @TABLE SELECT '10 Feb 2009 10:10:00'
INSERT INTO @TABLE SELECT '10 Feb 2009 10:40:00'
INSERT INTO @TABLE SELECT '10 Feb 2009 10:50:00'
INSERT INTO @TABLE SELECT '10 Feb 2009 11:00:00'

SELECT  *
FROM    @TABLE currentVal
WHERE   ((SELECT * FROM @TABLE nextVal WHERE DATEADD(mi,10,currentVal.DateValue) = nextVal.DateValue) IS NULL AND currentVal.DateValue != (SELECT MAX(DateValue) FROM @TABLE))
OR      ((SELECT * FROM @TABLE prevVal WHERE DATEADD(mi,-10,currentVal.DateValue) = prevVal.DateValue) IS NULL  AND currentVal.DateValue != (SELECT MIN(DateValue) FROM @TABLE))

答案 4 :(得分:0)

注意:使用MSSQL语法。我认为MySQL使用DATE_ADD(T1.date,INTERVAL 10 MINUTE)而不是DATEADD,但我没有测试过。

您可以通过两个自联接获取缺少的时间戳:

SELECT T1.[date] AS DateFrom, MIN(T3.[date]) AS DateTo
    FROM [test].[dbo].[WeatherData] T1
    LEFT JOIN [test].[dbo].[WeatherData] T2 ON DATEADD(MINUTE, 10, T1.date) = T2.date
    LEFT JOIN [test].[dbo].[WeatherData] T3 ON T3.date > T1.Date
    WHERE T2.[value] IS NULL
    GROUP BY T1.[date]

如果您有大量数据,可能需要尝试一次将范围限制为一个月,以避免服务器负载过重,因为此操作可能非常密集。

结果将是这样的:

DateFrom                    DateTo
2009-10-02 10:10:00.000 2009-10-02 10:40:00.000
2009-10-02 11:00:00.000 NULL

最后一行表示从上一个时间戳到未来的所有数据。

然后,您可以使用另一个联接从另一个数据库中获取时间戳介于这些时间间隔之间的行。

答案 5 :(得分:0)

此解决方案使用子查询,不需要任何显式临时表。我假设你的备份数据在另一台机器上的另一个数据库中;如果不是,您只需要为您需要的结果集执行第2步,并编写程序以相应地更新主数据库。

我们的想法是通过生成总结差距列表的“紧凑”结果集来开始。即以下数据:

MeasureDate
2009-12-06 13:00:00
2009-12-06 13:10:00
--missing data
2009-12-06 13:30:00
--missing data
2009-12-06 14:10:00
2009-12-06 14:20:00
2009-12-06 14:30:00
--missing data
2009-12-06 15:00:00

将转化为以下实际缺口严格位于(即排除)端点之间:

GapStart            GapEnd
2009-12-06 13:10:00 2009-12-06 13:30:00
2009-12-06 13:30:00 2009-12-06 14:10:00
2009-12-06 14:30:00 2009-12-06 15:00:00
2009-12-06 15:00:00 NULL

解决方案查询构建如下:

  1. 获取10分钟后没有条目的所有MeasureDate,因为这将是间隙的开始。注意:即使不是严格意义上的差距,也会包括最后一个条目;但这不会产生任何不利影响。
  2. 通过在间隙开始后使用第一个MeasureDate添加间隙的末尾来增加上述内容。
  3. 注意:间隙列表是紧凑的,除非您的碎片间隙的流行率非常高,否则在将结果集传递给备份计算机时不应占用太多带宽。
  4. 使用带有不等式的INNER JOIN来识别备份中可用的任何缺失数据。 (运行测试和检查以验证备份数据的完整性。)
  5. 假设您的备份数据合理,并且不会在测量中产生异常无根据的峰值,请将数据插入主数据库中。
  6. 应测试以下查询(出于性能原因,最好调整为在备份服务器上运行)。

    /* TiC Copyright
    This query is writtend (sic) by me, and cannot be used without 
    expressed (sic) written permission. (lol) */
    
    /*Step 3*/
    SELECT  gap.GapStart, gap.GapEnd,
            rem.MeasureDate, rem.Col1, ...
    FROM    (
            /*Step 2*/
            SELECT  gs.GapStart, (
                    SELECT  MIN(wd.MeasureDate)
                    FROM    WeatherData wd
                    WHERE   wd.MeasureDate > gs.GapStart
                    ) AS GapEnd
            FROM    (
                    /*Step 1*/
                    SELECT  wd.MeasureDate AS GapStart
                    FROM    WeatherData wd
                    WHERE   NOT EXISTS (
                            SELECT  *
                            FROM    WeatherData nxt
                            WHERE   nxt.MeasureDate = DATEADD(mi, 10, wd.MeasureDate)
                            )
                    ) gs
            ) gap
            INNER JOIN RemoteWeatherData rem ON
                rem.MeasureDate > gap.GapStart
            AND rem.MeasureDate < gap.GapEnd
    

    插入......

    INSERT INTO WeatherData (MeasureDate, Col1, ...)
    SELECT  /*gap.GapStart, gap.GapEnd,*/
            rem.MeasureDate, rem.Col1, ...
    ...
    

答案 6 :(得分:-1)

进行自我连接,然后计算较小且差异大于间隔的最大值。

在Oracle中我会这样做(ts是时间戳列):

Select t1.ts, max(t2.ts)
FROM atable t1 join atable t2 on t1.ts > t2.ts
GROUP BY t1.ts
HAVING (t1.ts - max(t2.ts))*24*60 > 10

有更好的方法来处理mySql中的差异计算,但我希望这个想法能够实现。

此查询将直接在停机之后和之前为您提供时间戳,您可以从那里进行构建。