运行窗口查询的总计

时间:2015-12-07 22:48:40

标签: sql postgresql postgis

我有一张表格如下:

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进行查询,它将返回总距离,移动所花费的总时间,停止的总时间,停止总次数和总空闲时间(空闲与停止时相同但停止时为更多的是5分钟的空闲时间而不是停止时间)。所有这一切都是白天,即使范围是2周。

一个人注意到,如果5分钟或更长的任何2个记录之间存在间隙,则表示设备已断电,应忽略该间隙期间的时间。

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

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');

1 个答案:

答案 0 :(得分:1)

在这上花了几个小时,并提出了这个胡言乱语的问题:

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没有提高速度。因此,使用四舍五入的行程距离(每gps读取间隔)将该交通拥堵时间解释为空闲时间。 ...

希望这会有所帮助