【问题标题】:SQL Query where I get most recent rows from timestamp from another tableSQL Query,我从另一个表的时间戳中获取最新的行
【发布时间】:2014-10-12 19:08:23
【问题描述】:

我在表格中输入了一些感官信息。我已经找到了能够准确告诉我特定设备的值何时发生变化的查询。

我需要知道当时所有其他传感器的状态。诀窍是,时间戳不会相等。我可以从传感器 1 获得一个数据点,然后 3 分钟后,从传感器 2 获得一个数据点,然后 30 秒后,从传感器 1 获得另一个数据点。

所以,这里是我所说的一个例子:

--- data_table ---

sensor | state | stime
-------+-------+---------------------
     1 |     A | 2014-08-17 21:42:00
     1 |     A | 2014-08-17 21:43:00
     2 |     B | 2014-08-17 21:44:00
     3 |     C | 2014-08-17 21:45:00
     2 |     D | 2014-08-17 21:46:00
     3 |     C | 2014-08-17 21:47:00
     1 |     B | 2014-08-17 21:48:00
     3 |     A | 2014-08-17 21:49:00
     2 |     D | 2014-08-17 21:50:00
     2 |     A | 2014-08-17 21:51:00

现在,我知道将向我传递状态更改的查询。我把它记下来了,它在视图中。该表如下所示:

 --- state_changed_view ---

sensor | state | stime
-------+-------+---------------------
     2 |     D | 2014-08-17 21:46:00
     1 |     B | 2014-08-17 21:48:00
     3 |     A | 2014-08-17 21:49:00
     2 |     A | 2014-08-17 21:51:00 

我想要的是一个 JOIN,我可以在其中获取“state_changed_view”的所有值,还可以在视图中的“sensor_timestamp”处获取其他相应传感器的值。

所以,理想情况下,我希望我的结果看起来像(或类似的东西):

sensor | state | stime               | sensor | state | stime
-------+-------+---------------------+--------+-------+---------------------
     2 |     D | 2014-08-17 21:46:00 |      1 |     A | 2014-08-17 21:43:00
     2 |     D | 2014-08-17 21:46:00 |      2 |     D | 2014-08-17 21:46:00
     2 |     D | 2014-08-17 21:46:00 |      3 |     C | 2014-08-17 21:45:00
     1 |     B | 2014-08-17 21:48:00 |      1 |     B | 2014-08-17 21:48:00
     1 |     B | 2014-08-17 21:48:00 |      2 |     D | 2014-08-17 21:46:00
     1 |     B | 2014-08-17 21:48:00 |      3 |     C | 2014-08-17 21:47:00
     3 |     A | 2014-08-17 21:49:00 |      1 |     B | 2014-08-17 21:48:00
     3 |     A | 2014-08-17 21:49:00 |      2 |     D | 2014-08-17 21:46:00 
     3 |     A | 2014-08-17 21:49:00 |      3 |     A | 2014-08-17 21:49:00 
     2 |     A | 2014-08-17 21:51:00 |      1 |     B | 2014-08-17 21:48:00 
     2 |     A | 2014-08-17 21:51:00 |      2 |     A | 2014-08-17 21:51:00 
     2 |     A | 2014-08-17 21:51:00 |      3 |     A | 2014-08-17 21:49:00

如您所见,我需要每个传感器的“data_table”中的最新行,对于存在于state_changed_view 中的每一行。

我只是不知道如何让 SQL 根据特定时间戳获取最新的行。

这是在 PL/pgSQL 系统上,所以任何与 Postgres 兼容的东西都很方便。

【问题讨论】:

  • PL/SQLOracle 的过程语言。你到底是什么意思?
  • 我们可以假设(sensor, stime) 的唯一约束吗?传感器的数量是动态的还是您在询问时知道它们?
  • 我提前知道了这个数字,所以是的,我们可以假设它是静态的。并且应该说 PL/pgSQL。

标签: sql postgresql join timestamp plpgsql


【解决方案1】:

查询

对于小型给定一组要检索的传感器(这适用于 Postgres 8.4 或更高版本):

SELECT c.sensor AS sensor_change
     , d1.state AS state_1, d1.stime AS stime_1
     , d2.state AS state_2, d2.stime AS stime_2
     , d3.state AS state_3, d3.stime AS stime_3
FROM  (
   SELECT sensor, stime
        , lag(state) OVER (PARTITION BY sensor ORDER BY stime)
           <> state AS change
        , max(CASE WHEN sensor = 1 THEN stime ELSE NULL END) OVER w AS last_1
        , max(CASE WHEN sensor = 2 THEN stime ELSE NULL END) OVER w AS last_2
        , max(CASE WHEN sensor = 3 THEN stime ELSE NULL END) OVER w AS last_3
   FROM   data d
   WINDOW w AS (ORDER BY stime)
   ) c
JOIN   data d1 ON d1.sensor = 1 AND d1.stime = c.last_1
JOIN   data d2 ON d2.sensor = 2 AND d2.stime = c.last_2
JOIN   data d3 ON d3.sensor = 3 AND d3.stime = c.last_3
WHERE  c.change
ORDER  BY c.stime;

完全不使用视图,直接在桌子上构建,这样更快。

这是假设(sensor, stime) 上的唯一索引是明确的。性能也很大程度上取决于这样的索引。

@Nick's solution 不同,在JOIN LATERAL(Postgres 9.3+)的基础上,这会返回一个单行,其中包含每次状态更改的所有值。

PL/pgSQL 函数

既然你提到了 PL/pgSQL,我希望这个(高度优化的)plpgsql 函数性能更好,因为它可以通过对表的单次顺序扫描来解决:

CREATE OR REPLACE FUNCTION f_sensor_change()
  RETURNS TABLE (sensor_change int   -- adapt to actual data types!
               , state_1 "char", stime_1 timestamp
               , state_2 "char", stime_2 timestamp
               , state_3 "char", stime_3 timestamp) AS
$func$
DECLARE
   r    data%rowtype;
BEGIN

FOR r IN
   TABLE data ORDER BY stime
LOOP
   CASE r.sensor
   WHEN 1 THEN  
      IF    r.state =  state_1 THEN  -- just save stime
         stime_1 := r.stime;
      ELSIF r.state <> state_1 THEN  -- save all & RETURN
         stime_1 := r.stime; state_1 := r.state;
         sensor_change := 1; RETURN NEXT;
      ELSE                           -- still NULL: init
         stime_1 := r.stime; state_1 := r.state;
      END IF;

   WHEN 2 THEN
      IF    r.state =  state_2 THEN
         stime_2 := r.stime;
      ELSIF r.state <> state_2 THEN
         stime_2 := r.stime; state_2 := r.state;
         sensor_change := 2; RETURN NEXT;
      ELSE
         stime_2 := r.stime; state_2 := r.state;
      END IF;

   WHEN 3 THEN
      IF    r.state =  state_3 THEN
         stime_3 := r.stime;
      ELSIF r.state <> state_3 THEN
         stime_3 := r.stime; state_3 := r.state;
         sensor_change := 3; RETURN NEXT;
      ELSE
         stime_3 := r.stime; state_3 := r.state;
      END IF;
   ELSE             -- do nothing, ignore other sensors
   END CASE;
END LOOP;

END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT * FROM f_sensor_change();

对重复使用有意义。相关答案:

SQL Fiddle for Postgres 9.3.
SQL Fiddle for Postgres 8.4.

【讨论】:

  • 我必须告诉你,这绝对令人印象深刻。它适用于我的少量传感器。我知道我需要升级 Postgres(几乎每次我在这里发帖时,我都会得到这样的评价),但升级将是巨大的。幸运的是,它很快就会到来。非常感谢你的工作。这太棒了。
  • @jasonmclose:请注意对查询的进一步简化。另外,我添加了一个 plpgsql 变体。我会很感兴趣哪个在你的情况下表现更好。您是否介意运行EXPLAIN ANALYZE 并在此处留下评论结果(最好是 5 或排除缓存工件)。
  • 谢谢。我会在今天或明天的某个时间尝试得到这个。我现在正处于时间紧缩状态。
【解决方案2】:

有几件事使这不那么直截了当:

  • 您想对每个state_changed_view 行进行子查询,但子查询必须从视图中提及相应的stime(以将其限制为较早的记录)。普通子查询不允许依赖外部字段,但您可以使用 lateral join 来完成此操作(至少从 Postgres 9.3 开始)。
  • 您不仅需要MAX(data_table.stime),还需要对应的data_table.state。您可以使用 另一个 嵌套查询来检索该行的其余部分,但 SELECT DISTINCT ON 为您提供了一种简单的方法来一次获取整个内容。

最终结果是这样的:

SELECT *
FROM
  state_changed_view,
  LATERAL (
    SELECT DISTINCT ON (sensor)
      sensor,
      state,
      stime
    FROM
      data_table
    WHERE
      data_table.stime <= state_changed_view.stime
    ORDER BY
      sensor,
      stime DESC
  ) a

【讨论】:

  • 感谢您的回复。不幸的是,我必须在 8.4 上运行,并且由于业务原因无法升级。这似乎总是一个问题,我的问题的答案总是很容易通过我的系统尚未支持的版本获得。
  • @jasonmclose:我在 8.4 中为您提供了解决方案,但您确实应该考虑升级。 8.4 has reached reaching EOL in July。顺便说一句,我喜欢这个优雅的解决方案。
【解决方案3】:

首先使用子查询查找每个传感器和状态的最大时间,该子查询对传感器和状态进行分组,然后将其加入视图

SELECT *
FROM 
(SELECT sensor, state, MAX(stime) as stime
from data_table
group by sensor, state) a
join state_changed_view on 1=1

【讨论】:

  • 当我在我的真实数据上尝试这个时,它返回的数据比我想要的映射多得多。虽然很接近。绝对比我迄今为止所拥有的更多。
猜你喜欢
  • 2011-12-04
  • 2018-03-23
  • 2021-12-27
  • 1970-01-01
  • 2014-04-05
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2021-06-12
相关资源
最近更新 更多