MySql计算事件的实例

时间:2013-06-25 13:40:56

标签: mysql if-statement case

所以我有一个每5分钟记录一次的事件日志,所以我的日志看起来像这样:

OK
Event1
Event1
Event1
OK
Event1
OK
Event1
Event1
Event1
OK

在这种情况下,我有3个“Event1”实例,因为它在返回该状态的时段之间有一个“OK”时段。

有没有一些像样的方法通过mySql处理这个? (注意,除了Event1 / OK之外还有其他常规出现的状态)

实际的Sql结构如下所示:

-Historical
    --CID //Unique Identifier, INT, AI
    --ID  //Unique Identifier for LOCATION, INT
    --LOCATION //Unique Identifier for Location, this is the site name, VarChar
    --STATUS //Pulled from Software event logger, VarChar
    --TIME //Pulled from Software event logger, DateTime

3 个答案:

答案 0 :(得分:1)

使用完全不同的方式做出另一个答案: -

SELECT MAX(@Counter) AS EventCount -- Get the max counter
FROM (SELECT @Counter:=@Counter + IF(status = 'OK' AND @PrevStatus = 1, 1, 0), -- If it is an OK record and the prev status was not an OK then add 1 to the counter
@PrevStatus:=CASE 
                WHEN status = 'OK' THEN @PrevStatus := 2 -- An OK status so save as a prev status of 2
                WHEN status != 'OK' AND @PrevStatus != 0 THEN @PrevStatus := 1 -- A non OK status but when there has been a previous OK status
                ELSE @PrevStatus:=0 -- Set the prev status to 0, ie, for a record where there is no previous OK status
            END
FROM (SELECT * FROM historical ORDER BY TimeStamp) a
CROSS JOIN (SELECT @Counter:=0, @PrevStatus := 0) b -- Initialise counter and store of prev status.
)c

这是使用用户变量。它有一个子选择,以正确的顺序返回记录,然后使用用户变量存储以前状态的代码。从0开始,当它发现状态为OK时,它将先前状态设置为2.如果它找到OK以外的状态,则将prev状态设置为1,但仅当prev状态不为0时(即,它)已经找到状态OK)。在存储prev状态代码之前,如果当前状态为OK且prev状态代码为1,则它将1添加到计数器,否则它将添加0(即,不添加任何内容)

然后它只是在外面选择一个选择计数器的最大值。

似乎工作但几乎无法读取!

编辑 - 处理多个ID

SELECT id, MAX(aCounter) AS EventCount -- Get the max counter for each id
FROM (SELECT id,
@PrevStatus:= IF(@Previd = id, @PrevStatus, 0), -- If the id has changed then set the store of previous status to 0
status,
@Counter:=IF(@Previd = id, @Counter + IF(status = 'OK' AND @PrevStatus = 1, 1, 0), 0) AS aCounter,  -- If it is an OK record and the prev status was not an OK and was for the same id then add 1 to the counter
@PrevStatus:=CASE 
                WHEN status = 'OK' THEN @PrevStatus := 2 -- An OK status so save as a prev status of 2
                WHEN status != 'OK' AND @PrevStatus != 0 THEN @PrevStatus := 1 -- A non OK status but when there has been a previous OK status
                ELSE @PrevStatus:=0 -- Set the prev status to 0, ie, for a record where there is no previous OK status
            END,
@Previd := id
FROM (SELECT * FROM historical ORDER BY id, TimeStamp) a
CROSS JOIN (SELECT @Counter:=0, @PrevStatus := 0, @Previd := 0) b
)c
GROUP BY id -- Group by clause to allow the selection of the max counter per id

哪个更不易读!

另一种选择,再次使用用户变量生成序列号: -

SELECT Sub1.id, COUNT(DISTINCT Sub1.aCounter) -- Count the number of distinct Sub1 records found for an id (without the distinct counter it would count all the recods between OK status records)
FROM (
    SELECT id,
    `TimeStamp`,
    @Counter1:=IF(@Previd1 = id, @Counter1 + 1, 0) AS aCounter, -- Counter for this status within id
    @Previd1 := id -- Store the id, used to determine if the id has changed and so whether to start the counters at 0 again
    FROM (SELECT * FROM historical WHERE status = 'OK' ORDER BY id, `TimeStamp`) a -- Just get the OK status records, in id / timestamp order
    CROSS JOIN (SELECT @Counter1:=0, @Previd1 := 0) b -- Initialise the user variables.
) Sub1
INNER JOIN (SELECT id,
    `TimeStamp`,
    @Counter2:=IF(@Previd2 = id, @Counter2 + 1, 0) AS aCounter,-- Counter for this status within id
    @Previd2 := id-- Store the id, used to determine if the id has changed and so whether to start the counters at 0 again
    FROM (SELECT * FROM historical WHERE status = 'OK' ORDER BY id, `TimeStamp`) a -- Just get the OK status records, in id / timestamp order
    CROSS JOIN (SELECT @Counter2:=0, @Previd2 := 0) b -- Initialise the user variables.
) Sub2
ON Sub1.id = Sub2.id -- Join the 2 subselects based on the id
AND Sub1.aCounter + 1 = Sub2.aCounter -- and also the counter. So Sub1 is an OK status, while Sub2 the the next OK status for that id
INNER JOIN historical Sub3 -- Join back against historical
ON Sub1.id = Sub3.id -- on the matching id
AND Sub1.`TimeStamp` < Sub3.`TimeStamp` -- and where the timestamp is greater than the timestamp in the Sub1 OK record
AND Sub2.`TimeStamp` > Sub3.`TimeStamp` -- and where the timestamp is less than the timestamp in the Sub2 OK record
GROUP BY Sub1.id -- Group by the Sub1 id

这只是为状态OK记录抓取表两次,每次添加一个序列号并匹配id匹配的位置,第二个副本上的序列号比第一个大1(即,它找到每个好的,紧接着它就OK了)。然后将其连接到id匹配的表,并且时间戳在2个OK记录之间。然后计算每个id的第一个计数器的不同出现次数。

这应该更具可读性。

答案 1 :(得分:0)

快速尝试,我感觉我错过了一个更好的方法来做到这一点,但认为这将有效。

SELECT COUNT(*)
FROM
(
    SELECT DISTINCT a.time, b.time
    FROM Historical a
    INNER JOIN Historical b
    ON a.time < b.time
    AND a.status = 'OK'
    AND b.status = 'OK'
    INNER JOIN Historical c
    ON a.time < c.time
    AND c.time < b.time
    AND c.status = 'Event1'
    LEFT OUTER JOIN Historical d
    ON a.time < d.time
    AND d.time < b.time
    AND d.status = 'OK'
    WHERE d.cid IS NULL
) Sub1

反复加入桌子。别名a和b应该用于OK事件,c表示这些日期之间的任何Event1事件。别名d在a和b之间寻找OK事件,如果找到任何事件,那么记录将被删除在WHERE子句中。

然后使用DISTINCT去除重复项。然后计算结果。

可能它可以简化为如下所示(尽管最好将日期转换为选择中的字符,如果这样做)

SELECT COUNT(DISTINCT CONCAT(a.time, b.time))
FROM Historical a
INNER JOIN Historical b
ON a.time < b.time
AND a.status = 'OK'
AND b.status = 'OK'
INNER JOIN Historical c
ON a.time < c.time
AND c.time < b.time
AND c.status = 'Event1'
LEFT OUTER JOIN Historical d
ON a.time < d.time
AND d.time < b.time
AND d.status = 'OK'
WHERE d.cid IS NULL

答案 2 :(得分:0)

当前一条记录为OK时,您想要计算的是事件的实例。您使用相关子查询识别这些,然后汇总以获取数字:

select status, count(*)
from (select h.*,
             (select h2.status
              from historical h2
              where h2.time < h.time
              order by h2.time desc
              limit 1
             ) as prevStatus
      from historical h
     ) h
where status <> 'OK' and (prevStatus = 'OK' or prevStatus is NULL)
group by status;

不清楚哪个列包含值OKEvent1。我猜它是status。我也不知道location扮演什么角色,但这至少应该让你开始。