运行 个窗口查询的总数
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 );
上提供了示例数据插入
我需要按日期范围查询 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 读数间隔)以将交通拥堵时间解释为空闲时间。 ...
希望这对您有所帮助
我有一个 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 );
上提供了示例数据插入
我需要按日期范围查询 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 读数间隔)以将交通拥堵时间解释为空闲时间。 ...
希望这对您有所帮助