一种提高以下查询性能的方法(更新表)

时间:2011-03-29 11:54:16

标签: mysql sql database

此问题与我前一段时间发布的问题有关,可在此处找到:Update values of database with values that are already in DB

我遇到以下情况:存储来自不同传感器的数据的表格(我总共有8个传感器)。该表的每一行都具有以下结构:

  

SensorID --- TimestampMS --- RawData --- Data

因此,例如,对于名为TEMPSensor1的温度传感器,我有以下内容:

  

TEMPSensor1 --- 1000 --- 200 --- 2
TEMPSensor1 --- 2000 --- 220 --- 2.2

等等,每个传感器(总共我8个)。我在阅读数据时遇到了一些问题,并且有些行数据“不正确”。确切地说,当rawdata字段为65535时,我应该更新该特定行。而我想要做的是将下一个值(及时)放到“损坏的数据”中。所以,如果我们有这个:

  

TEMPSensor1 --- 1000 --- 200 --- 2
TEMPSensor1 --- 2000 --- 220 --- 2.2
    TEMPSensor1 --- 3000 --- 65535 --- 655.35
TEMPSensor1 --- 4000 --- 240 --- 2.4

执行更新后,表格的内容应更改为:

  

TEMPSensor1 --- 1000 --- 200 --- 2
TEMPSensor1 --- 2000 --- 220 --- 2.2
    TEMPSensor1 --- 3000 --- 240 --- 2.4
TEMPSensor1 --- 4000 --- 240 --- 2.4

我最终做了以下事情:

UPDATE externalsensor es1
INNER JOIN externalsensor es2 ON es1.sensorid = es2.sensorid AND (es2.timestampms - es1.timestampms) > 60000 AND (es2.timestampms - es1.timestampms) < 120000  AND es1.rawdata <> 65535
SET es1.rawdata = es2.rawdata, es1.data = es2.data
WHERE es1.rawdata = 65535

因为我知道传感器的两次读取之间有60000到120000毫秒。但是,如果我有两个“损坏的”读数,那将无效。任何人都可以建议一种更有效地做到这一点的方法,不使用子查询选择,但加入?我的想法是让一个JOIN在它的时间戳之后为你提供该传感器的所有可能值,然后得到第一个,但我不知道如何限制JOIN结果。

欣赏。

3 个答案:

答案 0 :(得分:2)

这是一个没有相关子查询的解决方案,但是有一个三角形连接(不确定哪个更糟):

UPDATE externalsensor bad

  INNER JOIN (
    SELECT
      es1.SensorID,
      es1.TimestampMS,
      MIN(es2.TimestampMS) AS NextGoodTimestamp
    FROM externalsensor es1
      INNER JOIN externalsensor es2
        ON es1.SensorID = es2.SensorID AND
           es1.TimestampMS < es2.TimestampMS
    WHERE es1.RawData = 65535
      AND es2.RawData <> 65535
    GROUP BY
      es1.SensorID,
      es1.TimestampMS
  ) link ON bad.SensorID = link.SensorID AND
            bad.TimestampMS = link.TimestampMS

  INNER JOIN externalsensor good
    ON link.SensorID = good.SensorID AND
       link.NextGoodTimestamp = good.TimestampMS

SET
  bad.RawData = good.RawData,
  bad.Data = good.Data

假设时间戳在单个传感器组中是唯一的。

答案 1 :(得分:1)

由于您不想使用上一个问题的Dems解决方案,这里是JOIN的“解决方案”:

UPDATE myTable m
  JOIN myTable n
    ON m.SensorID = n.SensorID
      AND n.RawData <> 65535
      AND m.TimestampMS < n.TimestampMS
  JOIN myTable q
    ON n.SensorID = q.SensorID
      AND q.RawData <> 65535
      AND n.TimestampMS <= q.TimestampMS
SET
  m.RawData = n.RawData,
  m.Data = n.Data
WHERE
   m.RawData = 65535
;

修改

我上面的查询错了,错了。它似乎在我的测试数据库中工作,但逻辑是有缺陷的。我将在下面解释。

为什么上面的查询工作正常但是出错了:

首先,为什么这是错误的。

因为它不会为每个(sensorID,bad timestamp)组合返回一行,但会返回很多行。如果mm.TimestampMS)是我们想要查找的错误时间戳,它将返回该不良时间戳的所有组合以及稍后的好时间戳nq {{1 }}。如果找到这些n.TimestampMS <= q.TimestampMS时间戳的最小值,那将是一个正确的查询。

现在,为什么它在我的测试数据库中实际可行?

我认为这是因为MySQL在使用n时有很多选项(行),它只使用第一个选项。但幸运的是,我以增加的时间戳顺序添加了测试行,因此它们在数据库中以该顺序保存,并且(再次)幸运的是,这就是查询计划的安排方式(我推测)。

即使这个查询也适用于我的测试db:

SET ...

虽然出于同样的原因存在缺陷。

答案 2 :(得分:1)

一种完全不同的方法,使用贯穿整个表的过程(按每个传感器的降序时间顺序):

DELIMITER $$
CREATE PROCEDURE updateMyTable()
BEGIN
  SET @dummy := -9999 ;
  SET @dummy2 := -9999 ;
  SET @sensor := -999 ;

  UPDATE myTable m
    JOIN 
    ( SELECT n.SensorID
           , n.TimestampMS
           , @d := (n.RawData = 65535) AND (@sensor = n.SensorID) AS problem
           , @dummy  := IF(@d, @dummy, n.RawData) as goodRawData
           , @dummy2 := IF(@d, @dummy2, n.Data) as goodData
           , @sensor := n.SensorID AS previous
      FROM myTable n
      ORDER BY n.SensorID
             , n.TimeStampMS DESC
    ) AS upd
    ON m.SensorID = upd.SensorID
      AND m.TimeStampMS = upd.TimeStampMS
  SET m.RawData = upd.goodRawData
    , m.Data = upd.goodData
  WHERE upd.problem
  ;
END$$
DELIMITER ;