我有一张表格如下:
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 );
上提供的示例数据插入
我需要按日期范围和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');
答案 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读取间隔)将该交通拥堵时间解释为空闲时间。 ...
希望这会有所帮助