我有一个类似于以下内容的表 - 基本上包含时间戳以及其他一些列:
WeatherTable
+---------------------+---------+----------------+ +
| TS | MonthET | InsideHumidity | .... |
+---------------------+---------+----------------+ |
| 2014-10-27 14:24:22 | 0 | 54 | |
| 2014-10-27 14:24:24 | 0 | 54 | |
| 2014-10-27 14:24:26 | 0 | 52 | |
| 2014-10-27 14:24:28 | 0 | 54 | |
| 2014-10-27 14:24:30 | 0 | 53 | |
| 2014-10-27 14:24:32 | 0 | 55 | |
| 2014-10-27 14:24:34 | 9 | 54 | |
.......
我试图制定一个SQL查询,该查询返回特定时间范围内的所有行(此处没有问题),具有一定的任意粒度,例如每15秒。该数字始终以秒为单位指定,但不限于小于60的值。为了使事情进一步复杂化,时间戳不一定落在所需的粒度上,因此不是简单地选择时间戳的情况14:24:00,14:24:15,14:24:30等等 - 每个值的最接近时间戳的行都需要包含在结果中。
例如,如果开始时间为14:24:30,结束时间为14:32:00,粒度为130,则理想时间为:
14:24:30
14:26:40
14:28:50
14:31:00
但是,对于每个时间,可能不存在时间戳,在这种情况下,应该选择与每个理想时间戳具有最接近时间戳的行。如果两个时间戳同样远离理想时间戳,则应选择较早的时间戳。
数据库是Web服务的一部分,所以目前我只是忽略SQL查询中的粒度并稍后在(Java)代码中过滤掉不需要的结果。但是,就内存消耗和性能而言,这似乎并不理想。
有什么想法吗?
答案 0 :(得分:3)
您可以尝试这样做:
首先创建一个time_intervals列表。使用Get a list of dates between two dates中的存储过程make_intervals
创建一个临时表,以某种方式调用它:
call make_intervals(@startdate,@enddate,15,'SECOND');
然后,您将拥有一个表time_intervals
,其中包含两列名为interval_start
的列之一。使用它来查找每个区间最接近的时间戳,如下所示:
CREATE TEMPORARY TABLE IF NOT EXISTS time_intervals_copy
AS (SELECT * FROM time_intervals);
SELECT
time_intervals.interval_start,
WeatherTable.*
FROM time_intervals
JOIN WeatherTable
ON WeatherTable.TS BETWEEN @startdate AND @enddate
JOIN (SELECT
time_intervals.interval_start AS interval_start,
MIN(ABS(time_intervals.interval_start - WeatherTable.TS)) AS ts_diff
FROM time_intervals_copy AS time_intervals
JOIN WeatherTable
WHERE WeatherTable.TS BETWEEN @startdate AND @enddate
GROUP BY time_intervals.interval_start) AS min
ON min.interval_start = time_intervals.interval_start AND
ABS(time_intervals.interval_start - WeatherTable.TS) = min.ts_diff
GROUP BY time_intervals.interval_start;
这将找到每个time_interval最接近的时间戳。注意:WeatherTable
中的每一行都可以列出多次,如果使用的间隔小于存储数据的间隔的一半(或类似的东西,则得到点;) )。
注意:我没有测试查询,它们是从我的头脑中写的。请根据您的使用情况进行调整,并纠正可能存在的轻微错误......
答案 1 :(得分:1)
出于测试目的,我将数据集扩展到以下时间戳。我的数据库中的列名为time_stamp
。
2014-10-27 14:24:24
2014-10-27 14:24:26
2014-10-27 14:24:28
2014-10-27 14:24:32
2014-10-27 14:24:34
2014-10-27 14:24:25
2014-10-27 14:24:32
2014-10-27 14:24:34
2014-10-27 14:24:36
2014-10-27 14:24:37
2014-10-27 14:24:39
2014-10-27 14:24:44
2014-10-27 14:24:47
2014-10-27 14:24:53
我已经总结了这个想法,但在提供我能够解决的解决方案之前,让我更详细地解释一下。
要求是在给定时间内解决时间戳+/-。既然我们必须走向任何一个方向,我们就要花时间并将其分成两半。然后,时间帧的-1/2到时间帧的+1/2定义了一个" bin"考虑。
此MySQL语句给出了在给定开始时间内以@seconds
为间隔的给定时间的bin:
((floor(((t1.time_stamp - @time_start) - (@seconds/2))/@seconds) + 1) * @seconds)
注意:整个+ 1技巧是存在的,所以我们不会以-1索引的bin结束(它从零开始)。所有时间都是从开始时间计算出来的,以确保时间范围> = 60秒。
在每个bin中,我们需要知道每个时间帧与bin中心距离的大小。通过确定从开始起的秒数并从bin中减去它(然后取绝对值)来完成。
在这个阶段,我们随时都有#34; bining up"并在垃圾箱内订购。
要过滤掉这些结果,我们LEFT JOIN
到同一个表并设置条件以删除不需要的行。 LEFT JOIN
时,所需的行在NULL
ed表中将匹配LEFT JOIN
。
我更喜欢使用变量替换开头,结尾和秒,但仅限于可读性。 MySQL风格的注释包含在LEFT JOIN
ON
子句中,用于标识条件。
SET @seconds = 7;
SET @time_start = TIMESTAMP('2014-10-27 14:24:24');
SET @time_end = TIMESTAMP('2014-10-27 14:24:52');
SELECT t1.*
FROM temp t1
LEFT JOIN temp t2 ON
#Condition 1: Only considering rows in the same "bin"
((floor(((t1.time_stamp - @time_start) - (@seconds/2))/@seconds) + 1) * @seconds)
= ((floor(((t2.time_stamp - @time_start) - (@seconds/2))/@seconds) + 1) * @seconds)
AND
(
#Condition 2 (Part A): "Filter" by removing rows which are greater from the center of the bin than others
abs(
(t1.time_stamp - @time_start)
- (floor(((t1.time_stamp - @time_start) - (@seconds/2))/@seconds) + 1) * @seconds
)
>
abs(
(t2.time_stamp - @time_start)
- (floor(((t2.time_stamp - @time_start) - (@seconds/2))/@seconds) + 1) * @seconds
)
OR
#Condition 2 (Part B1): "Filter" by removing rows which are the same distance from the center of the bin
(
abs(
(t1.time_stamp - @time_start)
- (floor(((t1.time_stamp - @time_start) - (@seconds/2))/@seconds) + 1) * @seconds
)
=
abs(
(t2.time_stamp - @time_start)
- (floor(((t2.time_stamp - @time_start) - (@seconds/2))/@seconds) + 1) * @seconds
)
#Condition 2 (Part B2): And are in the future from the other match
AND
(t1.time_stamp - @time_start)
>
(t2.time_stamp - @time_start)
)
)
WHERE t1.time_stamp - @time_start >= 0
AND @time_end - t1.time_stamp >= 0
#Condition 3: All rows which have a match are undesirable, so those
#with a NULL for the primary key (in this case temp_id) are selected
AND t2.temp_id IS NULL
可能有一种更简洁的方式来编写查询,但它确实将结果过滤到了需要的,但有一个值得注意的例外 - 我故意放入一个重复的条目。此查询将返回这两个条目,因为它们符合所述标准。