如何在符合条件的行之间选择行?

时间:2015-10-01 18:29:31

标签: sql

这个问题是我工作中遇到的问题的简化版本。我使用支持所有标准SQL查询的自定义数据库。我想这个问题适用于任何支持SQL的RDMS。

假设我有表events,包括三列:event_type(字符串),details(字符串)和timestamp(整数)。

感兴趣的事件之一是停电。系统每隔一段时间记录一次电源状态。如果发生停电,"停电"将被记录。否则,"好"将被记录。

另一个感兴趣的事件是访问。每当有人进入房间时,输入的房间也会被记录下来。

我想选择停电期间输入的所有房间。

例如,我有以下行。

event_type | details        | timestamp
---------------------------------------
power      | good           | 14
access     | room 6         | 13
power      | good           | 12
access     | room 5         | 11
access     | room 4         | 10
power      | outage         | 9
power      | outage         | 8
access     | room 3         | 7
power      | outage         | 6
access     | room 2         | 5
power      | good           | 4
access     | room 1         | 3
power      | outage         | 2
access     | room 0         | 1
power      | good           | 0

我想获得以下行。

event_type | details        | timestamp
---------------------------------------

power      | good           | 12
access     | room 5         | 11
access     | room 4         | 10
power      | outage         | 9
power      | outage         | 8
access     | room 3         | 7
power      | outage         | 6

power      | good           | 4
access     | room 1         | 3
power      | outage         | 2

我这样做的当前方式感觉就像我没有以正确的方式使用SQL。

首先我得到断电的时间戳。接下来,我得到了停电后电源恢复的最小时间戳。最后,我选择两个时间戳之间的所有事件。之后,我发现电源恢复后的下一次停电时间戳。重复。

我做的另一种方法是将所有行下载到平面文件,然后使用Python脚本以过程方式过滤掉我想要的行。

有没有更好的方法来获取我需要的行?

3 个答案:

答案 0 :(得分:2)

您可以识别前一个“电源”行用于“停机”但不是“好”的所有行。

我认为这可以为您提供所需的信息。

select e.*
from events e
where 'outage' = (select e2.details
                  from events e2
                  where e2.event_type = 'power' and
                        e2.timestamp < e.timestamp
                  order by e2.timestamp desc
                  limit 1
                 ) or
      e.event_type = 'power';

答案 1 :(得分:2)

以下是您的工作查询(SQL Fiddle):

FileReader

我使用了SQL Server语法,但在MySQL中弹出SELECT E.* FROM dbo.Events E WHERE E.event_type IN ('access', 'power') AND ( SELECT TOP 1 details FROM dbo.Events E2 WHERE E.timestampid >= E2.timestampid AND E2.event_type = 'power' ORDER BY E2.timestampid DESC ) = 'outage' ; 同样容易。关于窗口函数,我现在没有时间或精力来提出答案,但如果你参考this question and my answer there,你会看到我用于类似问题的技巧之一

另外,请注意如何处理集合开头的数据是一个问题。如果第一个事件是LIMIT 1,您是否希望{ access, room 0 }, { power, outage }行在结果集中?即使room 0也可能是在集合开始时包含的候选者,因为可能是电源在开始时出现的情况 - 我们没有这些信息在这个数据中。如果您想在这两种情况中包含行,请告诉我,我可以更新我的查询。

答案 2 :(得分:0)

尝试以下代码。你可能需要使用第一个select语句返回的NULL值的默认值,当电源关闭并且还没有恢复时(你想要它是否显示?)。这很可能比使用更强大功能的实现(limit,row_number等)效率低。

SELECT event_type, details, timestamp
FROM events t1
WHERE
(event_type = 'power' AND details = 'outage')
OR
(SELECT MAX(timestamp) 
 FROM events t2 
 WHERE t2.event_type = 'power' AND 
       t2.details = 'outage' AND 
       t2.timestamp < t1.timestamp)
>
(SELECT MAX(timestamp)
 FROM events t2
 WHERE t2.event_type = 'power' AND
       t2.details = 'good' AND
       t2.timestamp < t1.timestamp)