这个问题是我工作中遇到的问题的简化版本。我使用支持所有标准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脚本以过程方式过滤掉我想要的行。
有没有更好的方法来获取我需要的行?
答案 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)