MySQL,返回X小时内的所有测量和结果

时间:2013-02-11 12:51:16

标签: mysql performance join

这个问题与我之前提出的问题非常相关:MySQL, return all results within X last hours尽管还有其他重大限制:

现在我有2个表,一个用于测量,一个用于部分测量的分类结果。

测量结果不断到达,结果是在新测量分类后不断添加。

结果不一定以测量的到达和存储顺序的相同顺序存储!

我只对展示最后结果感兴趣。通过 last 我的意思是取最后一次可用结果的最大时间(时间是测量结构的一部分)称之为并且X秒的范围,并且呈现测量结果与可用一起产生Y和YX范围内的结果。

以下是2个表格的结构:

事件表:

CREATE TABLE `event_data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `Feature` char(256) NOT NULL,
  `UnixTimeStamp` int(10) unsigned NOT NULL,
  `Value` double NOT NULL,

  KEY `ix_filter` (`Feature`),
  KEY `ix_time` (`UnixTimeStamp`),
  KEY `id_index` (`id`)
) ENGINE=MyISAM

分类结果表:

CREATE TABLE `event_results` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `level` enum('NORMAL','SUSPICIOUS') DEFAULT NULL,
  `score` double DEFAULT NULL,
  `eventId` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `eventId_index` (`eventId`)
) ENGINE=MyISAM

我无法首先查询最后一次测量时间戳,因为我想要显示当前结果的测量结果,并且由于测量结果不断到达,结果可能仍然无法获得。

因此我想到用两个表加入 event_results.eventId=event_data.id并且选择event_data.UnixTimeStamp as maxTime的最大时间,在我拥有maxTime之后,我需要再次执行相同的操作(连接2个表)并在where子句中添加条件

WHERE event_data.UnixTimeStamp >= maxTime + INTERVAL -X SECOND

执行2个连接似乎效率不高只是为了实现我的要求,你有更多的ef

2 个答案:

答案 0 :(得分:1)

根据我的理解,您使用的是聚合函数MAX。这将生成一个大小为1的记录集,这是您执行的最长时间。因此,需要将其分解为子查询(如您所说,嵌套选择)。你必须在某个时候做2个查询。 (通过子查询/嵌套选择,您对上一个问题的答案中有2个查询。)

子查询导致问题的主要时间是在查询的select部分中执行子查询时,因为每次有行时都会执行子查询,这会使查询在结果集增长时以指数方式运行。让我们回答你的上一个问题并以一种可怕,低效的方式写下来:

SELECT timeStart, 
       (SELECT max(timeStart) FROM events) AS maxTime
FROM events
WHERE   timeStart > (maxTime + INTERVAL -1 SECOND)

对于max eventtime,每次有eventTime记录时,都会执行select查询。它应该产生相同的结果,但这很慢。这就是对子查询的恐惧来自的地方。

它还在每一行上执行聚合函数MAX,每次都会返回相同的答案。因此,您执行该子查询ONCE而不是每行。

但是,对于上一个问题的答案,MAX子查询部分运行一次,并用于过滤选择运行一次的位置。因此,总共运行了2个查询。

2个超快速查询比1个超慢速查询超快速运行更快。

答案 1 :(得分:1)

我不完全确定你想要返回什么结果集,所以我将做一些假设。请随时纠正我所做的任何假设。

听起来(对我而言)你希望event_data中的所有行在绝对“最新”时间戳的一小时(或多秒)内,以及这些行,你也想要返回来自event_results的任何相关行,如果有任何匹配的行可用。

如果是这种情况,那么使用内联视图来检索时间戳的最大值是要走的路。 (该操作将非常高效,因为查询将返回单行,并且可以从现有索引中有效地检索它。)

由于您希望所有行都在指定的时间段内(从“最新时间”返回到“最新时间减去X秒”),我们可以继续计算同一查询中句点的起始时间戳。在这里,我们假设你想“回去”一小时(= 60 * 60秒):

SELECT MAX(UnixTimeStamp) - 3600 FROM event_data

注意:上面SELECT列表中的表达式基于定义为整数类型的UnixTimeStamp列,而不是DATETIME或TIMESTAMP数据类型。如果列被定义为DATETIME或TIMESTAMP数据类型,我们可能会用以下内容表示:

SELECT MAX(mydatetime) + INTERVAL -3600 SECONDS

(我们可以用分钟,小时等来指定间隔单位)

我们可以在另一个查询中使用该查询的结果。要在相同的查询文本中执行此操作,我们只需将该查询包装在括号中,并将其作为行源引用,就好像该查询是实际的表一样。这允许我们从指定时间段内的event_data获取所有行,如下所示:

SELECT d.id
     , d.Feature
     , d.UnixTimeStamp
     , d.Value
  JOIN ( SELECT MAX(l.UnixTimeStamp) - 3600 AS from_unixtimestamp
           FROM event_data l
       ) m
  JOIN event_data d
    ON d.UnixTimetamp >= m.from_unixtimestamp

在这种特殊情况下,外部查询中的UnixTimeStamp列不需要上限谓词。这是因为我们已经知道没有UnixTimeStamp的值大于MAX(UnixTimeStamp),这是我们感兴趣的时期的上限。

(我们可以在内联视图的SELECT列表中添加一个表达式,返回MAX(l.UnixTimeStamp) AS to_unixtimestamp,然后在外部查询中包含类似AND d.UnixTimeStamp <= m.to_unixtimestamp的谓词,但这将是不必要的冗余。)

您还指定了从event_results表返回信息的要求。

我相信你说你想要任何“可用”的相关行。这表明(对我而言)如果event_results中没有匹配的行“可用”,您仍然希望从event_data表中返回该行。

我们可以使用LEFT JOIN操作来实现:

SELECT d.id
     , d.Feature
     , d.UnixTimeStamp
     , d.Value
     , r.id
     , r.level
     , r.score
     , r.eventId
  JOIN ( SELECT MAX(l.UnixTimeStamp) - 3600 AS from_unixtimestamp
           FROM event_data l
       ) m
  JOIN event_data d
    ON d.UnixTimetamp >= m.from_unixtimestamp
  LEFT
  JOIN event_results r
    ON r.eventId = d.id

由于eventID表中的event_results列没有唯一约束,因此有可能找到来自event_results的多个“匹配”行。每当发生这种情况时,event_data表中的行将重复一次,对于来自event_results的每个匹配行,将重复一次。

如果event_results中没有匹配的行,则仍将返回event_data中的行,但event_results表中的列设置为NULL。

为了提高性能,请从SELECT列表中删除不需要返回的任何列,并在ORDER BY子句中选择表达式时明智。 (增加覆盖指数可以提高性能。)

对于上面所写的语句,MySQL可能会使用ix_time表上的event_data索引和eventId_index表上的event_results索引。