SQL查询,我从另一个表的时间戳中获取最新的行

时间:2014-08-19 15:38:59

标签: sql postgresql join timestamp plpgsql

我有一些感官信息进入一张桌子。我已经找到了能够准确告诉我特定设备的值何时发生变化的查询。

我需要知道的是当时所有其他传感器的状态。诀窍是,时间戳不会相等。我可以从传感器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

正如您所看到的,对于state_changed_view中存在的每一行,我需要每个传感器的'data_table'中的最新行。

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

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

3 个答案:

答案 0 :(得分:3)

查询

要检索的给定传感器集(适用于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)上的UNIQUE INDEX是明确的。性能也在很大程度上取决于这样的指数。

与基于JOIN LATERAL(Postgres 9.3+)的@Nick's solution相反,这会返回单行,其中包含州内每次更改的所有值。

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.

答案 1 :(得分:1)

有一些事情使得这不那么直截了当:

  • 您希望为每个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

答案 2 :(得分:0)

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

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