我的问题:
我有一个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分钟),除了一些只是缺失。
好的,临时表怎么样?是否有一种优雅的方式自动填充时间范围?如果两个脚本试图同时运行,这会导致表格出现问题吗?
答案 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
获取缺少的时间戳 - 这个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个输入变量:
...这意味着我们可以使用原始的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
解决方案查询构建如下:
应测试以下查询(出于性能原因,最好调整为在备份服务器上运行)。
/* 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中的差异计算,但我希望这个想法能够实现。
此查询将直接在停机之后和之前为您提供时间戳,您可以从那里进行构建。