【问题标题】:SQL Query for current state of all entities in event tableSQL查询事件表中所有实体的当前状态
【发布时间】:2018-01-15 23:57:52
【问题描述】:

我有一个表,其中包含 N 个设备的事件数据,每个设备可能会或可能不会在给定时间将数据添加到表中。我需要查询表中每个不同设备的最后状态,以及设备的先前状态。

示例表数据:

id   | device    | event_data  | date_time
________________________________________________
1019 | sensor501 | open        | 2018-01-05T00:05:15Z
1020 | sensor509 | closed      | 2018-01-05T00:05:16Z
1021 | sensor501 | reset       | 2018-01-05T00:05:18Z
1022 | sensor501 | closed      | 2018-01-05T00:05:19Z
1023 | sensor974 | open        | 2018-01-05T00:05:27Z
1024 | sensor729 | closed      | 2018-01-05T00:06:07Z
1025 | sensor729 | reset       | 2018-01-05T00:06:46Z

示例结果数据:

device    | event_data  | prev_event_data | date_time
________________________________________________
sensor501 | closed      | reset           | 2018-01-05T00:05:19Z
sensor509 | closed      | null            | 2018-01-05T00:05:16Z
sensor974 | open        | null            | 2018-01-05T00:05:27Z
sensor729 | reset       | closed          | 2018-01-05T00:06:46Z

您将如何以最有效的方式查询示例表以获取示例结果数据?

【问题讨论】:

  • 你使用什么 SQL 引擎?
  • @juergend 它是 MySQL 的一个变种
  • 顺便说一句,这是这个标签下最常见的问题

标签: mysql sql


【解决方案1】:

这可以通过相关的子查询来完成。先获取最新的event_data,再获取最新的上一行。

select t1.device,t1.event_data,t1.date_time,
(select t2.event_data from tbl t2 
 where t1.device=t2.device and t1.date_time>t2.date_time 
 order by t2.date_time desc limit 1) as prev_event_data
from tbl t1
where t1.date_time = (select max(t2.date_time)
                      from tbl t2
                      where t1.device=t2.device
                     )

如果您切换到支持窗口函数的 dbms 版本,请使用

select device,event_data,prev_event_data,date_time
from (select t.*
      ,lead(event_data) over(partition by device order by date_time) as next_event_data
      ,lag(event_data) over(partition by device order by date_time) as prev_event_data
      from tbl t
     ) t 
where next_event_data is null -- last row in the group which will be the row with latest date_time

【讨论】:

  • 花了我一段时间来测试,但这很完美。感谢您的帮助!
【解决方案2】:

您可以在where 中使用相关子查询:

select e.*
from example e
where e.date_time = (select max(e2.date_time)
                     from example e2
                     where e2.device = e.device
                    );

如果您知道ids 是按顺序插入的,那么您可以使用id 代替date_time 进行过滤。

如果您在(device, date_time) 上有索引,这应该会有很好的性能。

您可以通过使用相关子查询来获取之前的状态:

select e.*,
       (select e2.event_data
        from example e2
        where e2.device = e.device and
              e2.date_time < e.date_time
        order by e2.date_time desc
        limit 1
       ) as prev_event_data
from example e
where e.date_time = (select max(e2.date_time)
                     from example e2
                     where e2.device = e.device
                    );

编辑:

如果这些是您需要的唯一列,那么 substring_index()/group_concat() hack 可能会起作用:

select max(id), device, max(date_time),
       substring_index(group_concat(event_data order by date_time desc), ',', 1) as last_event_data,
       substring_index(substring_index(group_concat(event_data order by date_time desc), ',', 2), ',', -1) as prev_event_data
from example
group by device;

在大多数其他数据库中,您只需使用 lag() 函数即可,但大多数 MySQL 版本不支持此功能。

请注意,这取决于group_concat() 函数。默认情况下,它的最大内部字符串长度为 1028。但是,这是一个可设置的参数,可以做得更大。

【讨论】:

  • 如何从这个查询中获取之前的事件数据?
猜你喜欢
  • 1970-01-01
  • 2020-10-09
  • 1970-01-01
  • 2013-05-06
  • 2020-02-07
  • 1970-01-01
  • 1970-01-01
  • 2018-09-24
  • 1970-01-01
相关资源
最近更新 更多