优化隐式自连接查询

时间:2018-01-25 12:15:13

标签: mysql sql

假设我有下表log。它描述了一系列与序列号相关的带时间戳的事件。有各种事件类型列,此处只显示了2列 - session_startedvoltage_changed。在每一行中,只有一个非空的事件类型。所有行都包含非空serialtime_stamp字段。第一行将两种事件类型都设置为NULL,这意味着其他事件类型列之一(未显示)包含一个值(它有助于代表性示例)。

我想找到发生的每个session_started事件,记录下一个voltage_changed值(按时间戳记)。这是数据:

serial ||     time_stamp        || session_started || voltage_changed

BBBB    |  2017-12-15 03:05:55  |        NULL      |        NULL    |
AAAA    |  2017-12-15 04:05:55  |        1         |        NULL    |
AAAA    |  2017-12-15 04:30:55  |        NULL      |        127     |
AAAA    |  2017-12-15 05:15:55  |        NULL      |        75      |
BBBB    |  2017-12-15 05:20:55  |        1         |        NULL    |
BBBB    |  2017-12-15 06:00:55  |        NULL      |        10      |

期望的结果:

serial ||     time_stamp        ||    voltage

AAAA    |  2017-12-15 04:05:55  |       127        |
BBBB    |  2017-12-15 05:20:55  |       10         |

这是我试过的查询。它在这个示例表上工作并生成正确的结果,但是在整个表上运行需要很长时间(我厌倦了等待查询完成执行...)完整表有190,000行并且有一个索引on time_stamp。

SELECT 
   h.serial, 
   h.time_stamp,
   hh.voltage_changed AS voltage 
FROM 
   log h, 
   log hh 
WHERE 
   h.serial = hh.serial 
   AND hh.time_stamp = (SELECT MIN(hh.time_stamp) 
                        FROM  log hh 
                        WHERE (hh.time_stamp >= h.time_stamp) 
                        AND hh.voltage_changed IS NOT NULL 
                        AND (h.session_started = 1));

有没有办法优化此查询以便在大型表上更有效地工作?在time_stamp上有一个索引就足够了,还是应该考虑这个实例中的其他列?

2 个答案:

答案 0 :(得分:2)

因为您只想要一列,我认为自连接是不必要的。我首先将其写为相关子查询:

select l.*,
       (select l2.voltage_changed
        from log l2
        where l2.serial = l.serial and
              l2.time_stamp >= l.time_stamp and
              l2.voltage_changed is not null
        order by l2.time_stamp asc
        limit 1
       ) as voltage_changed
from log l
where l.session_started = 1;

为此,您需要两个索引。更重要的是log(serial, voltage_changed, time_stamp)。第二个是log(session_started, serial)

答案 1 :(得分:1)

我会将您的数据重新整理为voltage_changed结构,以便您在event_type列中包含event_valueevent_type或其他值,以及与该关联的任何整数值serial列中的事件。由serialvoltage_changed编制索引,select t1.serial, t1.time_stamp, t2.event_value as voltage from ( select e1.serial, e2.time_stamp, min(e3.timestamp) AS voltage_ts from log e1 left join log e2 on e1.serial=e2.serial and e1.time_stamp<=e2.time_stamp and e2.event_type='voltage_changed' where e1.event_type='session_started' and e1.event_value=1 group by 1,2 ) t1 join log t2 on t1.serial=t2.serial and t1.voltage_ts=t2.time_stamp and t2.event_type='voltage_changed'; 为聚集索引。

在这种情况下,必须有效地过滤必要的行,而不必扫描整个表中的O(1)列的非空值以进一步自我加入。

然后你的查询就像(与相关子查询一起工作):

HashMap

我知道保留这些数据可能是有效的考虑因素,只是提供另一种观点。