运行 个窗口查询的总数

Running totals for a windowed query

我有一个 table 看起来像:

CREATE TABLE gps_history (
  id integer PRIMARY KEY,
  device_id integer,
  date_time timestamp without time zone,
  speed integer,
  lat double precision,
  lng double precision );

pastbin

上提供了示例数据插入

我需要按日期范围查询 device_id,这将 return 总距离、移动总时间、停止总时间、停止总次数和空闲总时间 ( idle 与 stopped 相同,但当停止时间超过 5 分钟时,会累积 idle 时间而不是停止时间)。所有这些都是按天计算的,即使范围是 2 周。

一个值得注意的怪癖,如果任何 2 条记录之间有 5 分钟或更长时间的间隔,这意味着设备已关闭电源并且应该忽略该间隔期间的时间。

到目前为止,我所拥有的是一个窗口化查询,因此我可以回头确定停止/移动状态以及为哪些状态累积时间。我遇到的主要问题是能够说出空闲时间,因为先前记录中的时间已经添加到停止时间而不是空闲时间。

SELECT device_id,
         to_char(date_time, 'YYYY-MM-DD') as period,
         sum(distance) AS total_distance,
         sum(time_diff) as total_time,
         sum(stop_time) as stop_time,
         sum(move_time) as move_time,
         sum(is_new_stop) as total_stops
FROM (
   SELECT device_id,
          date_time,
          lat,
          lng,
          speed,

          CASE
            WHEN lag(id, 1) OVER (ORDER BY date_time) IS NULL --is first row
                THEN 0
            ELSE
                ST_Distance(ST_SetSRID(ST_MakePoint(lng, lat),4326), lag(ST_SetSRID(ST_MakePoint(lng, lat) ,4326), 1) OVER (ORDER BY date_time))
          END AS distance,

        CASE
            WHEN lag(id, 1) OVER (ORDER BY date_time) IS NULL --is first row
                THEN 0
            WHEN  EXTRACT(EPOCH FROM ( date_time - lag(date_time, 1) OVER (ORDER BY date_time) )) > 300 --longer than 5 mins so 0
                THEN 0
            WHEN speed > 0 and (lag(speed, 1) OVER (ORDER BY date_time)) > 0
                THEN EXTRACT(EPOCH FROM ( date_time - lag(date_time, 1) OVER (ORDER BY date_time) ))
            ELSE 0
        END AS move_time, -- last rec and this one moving, so accrue move time

        CASE
            WHEN lag(id, 1) OVER (ORDER BY date_time) IS NULL --is first row
                THEN 0
            WHEN  EXTRACT(EPOCH FROM ( date_time - lag(date_time, 1) OVER (ORDER BY date_time) )) > 300 --longer than 5 mins so 0
                THEN 0
            WHEN speed = 0 and (lag(speed, 1) OVER (ORDER BY date_time)) = 0
                THEN EXTRACT(EPOCH FROM ( date_time - lag(date_time, 1) OVER (ORDER BY date_time) ))
            ELSE 0
        END AS stop_time, -- last rec and this one 0 speed, so accrue stop time

        CASE
            WHEN lag(id, 1) OVER (ORDER BY date_time) IS NULL --is first row
                THEN 0
            WHEN  EXTRACT(EPOCH FROM ( date_time - lag(date_time, 1) OVER (ORDER BY date_time) )) > 300 --longer than 5 mins so 0
                            THEN 0
            ELSE
                EXTRACT(EPOCH FROM ( date_time - lag(date_time, 1) OVER (ORDER BY date_time) ))
        END AS time_diff, -- time diff since last record

         CASE
            WHEN lag(id, 1) OVER (ORDER BY date_time) IS NULL and speed = 0--is first row
                THEN 1
            WHEN  EXTRACT(EPOCH FROM ( date_time - lag(date_time, 1) OVER (ORDER BY date_time) )) > 300 and speed = 0
                THEN 1
            WHEN lag(speed, 1) OVER (ORDER BY date_time) > 0 and speed = 0
                THEN 1
            ELSE
                0
        END AS is_new_stop -- time diff since last record


   FROM   gps_history
   WHERE device_id = 5000 and date_time >= '2015-12-7' and date_time < '2015-12-8'
   ORDER BY date_time
   ) sub
GROUP BY device_id, to_char(date_time, 'YYYY-MM-DD');

为此花了几个小时,提出了这个乱码查询:

with
   main as (select *  FROM   gps_history WHERE device_id = 5000 and date_time >= '2015-08-10' and date_time < '2015-10-30' ORDER BY date_time)
  ,main_1 as(select *, lag(date_time) over(partition by device_id order by device_id, date_time) l_date_time, lag(speed) over(partition by device_id order by device_id, date_time) l_speed 
                 , coalesce( ST_Distance(ST_SetSRID(ST_MakePoint(lng, lat),4326), lag(ST_SetSRID(ST_MakePoint(lng, lat) ,4326), 1) OVER (ORDER BY date_time) ),0) :: numeric(30,4) as distance
from main)
,main_2 as (select *, case WHEN  EXTRACT( EPOCH FROM (date_time -l_date_time) ) > 300  THEN -1
        when EXTRACT( EPOCH FROM (date_time -l_date_time)) < 300 and speed = 0 then 0
        when EXTRACT( EPOCH FROM (date_time -l_date_time)) < 300 and speed > 0 then 1
        else -1
        end::integer as state ,           
        coalesce(  EXTRACT( EPOCH FROM (date_time -l_date_time)), 0) time_elapsed
from main_1)

,main_3 as( select device_id, date_time,speed,state,distance,time_elapsed  ,sum(distance ) over (order by date_time ) ::numeric(30,4) travelled , case when state in (-1,0) and lag( state) over(order by date_time) =1 then 1 else 0 end stops    from main_2)
,main_4 as( select device_id , max(date_time) date_time , speed  , state , sum(distance) distance ,  sum(time_elapsed) time_elapsed,  travelled , sum(stops) as stops from main_3 group by device_id  ,state  ,speed ,travelled order by date_time)

 select 
  sum( case when state in (-1) then  time_elapsed else 0 end) offline_time
 ,sum( case when state in (0,1) then  time_elapsed else 0 end) total_time
 ,sum( case when state in (1) then  time_elapsed else 0 end) move_time
 ,sum( case when state in (0) and time_elapsed <=300 then  time_elapsed else 0 end) stop_time
 ,sum( case when state in (0) and time_elapsed > 300 then  time_elapsed else 0 end) idle_time
 ,sum( stops) stops
 ,sum(distance) distance 
 from main_4

根据您发现的数据,您需要稍微更改它以满足您的需要。因为当汽车静止时 gps 读数会波动,必须对其进行四舍五入以使一些查询逻辑起作用,并且发现没有时间范围让汽车实际静止超过 5 分钟,根据 gps 位置读数,似乎汽车被咳嗽了一些交通堵塞和缓慢移动和 gps 没有加快速度。因此,将行驶距离四舍五入到 4 位小数(每个 gps 读数间隔)以将交通拥堵时间解释为空闲时间。 ...

希望这对您有所帮助