MySQL - 如何将数据与前一行进行比较?

时间:2014-08-18 17:54:17

标签: mysql aggregation

我有一张包含以下数据的表格:

mysql> select * from playground order by macAddress, locatedAt;
+------------+---------------------+--------------+
| macAddress | locatedAt           | status       |
+------------+---------------------+--------------+
| device1    | 2014-08-11 01:20:27 | connected    |
| device1    | 2014-08-11 01:30:27 | connected    |
| device1    | 2014-08-11 01:40:27 | disconnected |
| device1    | 2014-08-11 01:49:27 | connected    |
| device1    | 2014-08-11 01:50:27 | disconnected |
| device1    | 2014-08-11 01:55:27 | disconnected |
| device1    | 2014-08-11 02:05:27 | disconnected |
| device1    | 2014-08-11 02:17:27 | disconnected |
| device1    | 2014-08-11 02:27:27 | disconnected |
| device1    | 2014-08-11 02:47:27 | connected    |
| device1    | 2014-08-11 02:57:27 | connected    |
| device1    | 2014-08-11 03:50:27 | disconnected |

我需要总结如下:

date         | hour | connected | disconnected | previouslyConnected
2014-08-11   |  1   |     1     |      0       |      0
2014-08-11   |  2   |     1     |      0       |      0
2014-08-11   |  3   |     0     |      0       |      1

只要设备在一小时内连接一次,断开连接无关紧要。如果设备在给定小时内仅具有“断开连接”条目,则它处于“断开连接”状态。如果设备断开连接,则如果同一设备在前一小时处于“已连接”状态,则它将进入先前连接状态。否则它本身就处于“断开连接”状态。

在第三行中,device1处于“previousConnected”状态,因为此设备在前一小时(小时2)已连接。

我无法获取'previousConnected'状态设备;

使用我当前的查询,我可以获得除最后一列之外的所有内容。

SELECT TEMP.day, TEMP.hour, 
    SUM(CASE WHEN TEMP.connected > 0 THEN 1 ELSE 0 END) as connected,
    SUM(CASE WHEN TEMP.unconnected > 0 AND TEMP.connected < 1 THEN 1 ELSE 0 END) as unconnected
    from 

    (SELECT
        DATE(locatedAt) day,
        HOUR(locatedAt) hour,
        macAddress,
        SUM(CASE WHEN status='connected' THEN 1 ELSE 0 END) as connected,
        SUM(CASE WHEN status='disconnected' THEN 1 ELSE 0 END) as unconnected
    FROM
        playground
    GROUP BY 
        DATE(locatedAt), HOUR(locatedAt), macAddress) as TEMP

    GROUP BY TEMP.day, TEMP.hour;

任何指针?

3 个答案:

答案 0 :(得分:0)

您可以使用用户定义的变量来检查以前的结果状态,在内部查询中我使用GROUP_CONCATlocatedAt的降序连接所有状态,因此它将生成逗号按降序排列的状态字符串并使用SUBSTRING_INDEX我选择了第一个状态,这是机器的最后一个状态,我已经存储了表达式SUBSTRING_INDEX(GROUP_CONCAT(状态{{1}的最后一个状态在ORDER BY locatedAt DESC),',',1)变量中,以及一个@prev语句来检查它是否在前一行中连接了show 1 else 0

CASE

Fiddle Demo

答案 1 :(得分:0)

假设:

  • 您希望在特定小时连接列
  • 时显示1
  • 如果在特定小时内有连接,则要显示0 - 已断开连接的列
  • 您希望在前一小时有连接时显示1,但在当前小时仍未断开连接时显示1 - 先前已连接的列
  • 您希望在断开连接时显示1,而不是在最后一小时 - 断开连接的列中显示先前的连接

QUERY:

SELECT 
    day,
    hour,
    connected,
    disconnected,
    previouslyConnected
FROM
(   SELECT 
        t.day,
        t.hour,
        CASE WHEN t.connected > 0 THEN 1 ELSE 0 END AS connected,
        IF((CASE WHEN t.connected > 0 THEN 1 ELSE 0 END) = 0 AND @A = 0, 1, 0) AS disconnected,
        IF((CASE WHEN t.connected > 0 THEN 1 ELSE 0 END) = 0 AND @A = 1, 1, 0) AS previouslyConnected,
        @A := CASE WHEN t.connected > 0 THEN 1 ELSE 0 END
    FROM
    (   SELECT 
        DATE(locatedAt) AS 'day',
        HOUR(locatedAt) AS 'hour',
        SUM(CASE WHEN status = 'connected' THEN 1 ELSE 0 END) AS connected,
        SUM(CASE WHEN status = 'disconnected' THEN 1 ELSE 0 END) AS unconnected
    FROM playground
    GROUP BY HOUR(locatedAt) , DATE(locatedAt)
    ) t
) t1;

DEMO

输出:

+-------------+------+-----------+--------------+---------------------+
|date         | hour | connected | disconnected | previouslyConnected |
+-------------+------+-----------+--------------+---------------------+
|2014-08-11   |  1   |     1     |      0       |      0              |
|2014-08-11   |  2   |     1     |      0       |      0              |
|2014-08-11   |  3   |     0     |      0       |      1              |
+-------------+------+-----------+--------------+---------------------+

答案 2 :(得分:0)

你可以在没有@variables的情况下通过LEFT JOIN前一个小时来完成:

    SELECT DATE(p.locatedAt) day,
           HOUR(p.locatedAt) hour,
           SUM(p.status='connected') != 0 connected,
           SUM(p.status='disconnected') 
             AND NOT SUM(p.status='connected') unconnected,
           SUM(p.status='disconnected') 
             AND NOT SUM(p.status='connected')         
             AND COALESCE(SUM(pp.connected),0) != 0 previously_connected
      FROM playground p
 LEFT JOIN (
      SELECT EXTRACT(DAY_HOUR FROM locatedAt) d_h,
             SUM(status='connected') connected
        FROM playground
    GROUP BY EXTRACT(DAY_HOUR FROM locatedAt),
             macAddress
           ) pp
        ON pp.d_h = EXTRACT(DAY_HOUR FROM p.locatedAt) - 1
  GROUP BY day,
           hour
  ORDER BY day,
           hour