I have a big table with sensor measurements that has this structure:
create table measurements (
sensor_id varchar(50) not null,
timestamp timestamptz not null,
value decimal(10, 6) not null,
primary key (sensor_id, timestamp)
)
It contains measurements from sensors (rain gauges, but that doesn't really matter) taken at 5 minute intervals. Sensor values can be 0 or positive, but not negative. Currently the dataset goes back a bit more than one year, but it should be able to handle years of data.
I want to retrieve the most recent measurements per sensor for further analysis, such that I get at least two weeks worth of measurements, and the set of measurements should contain at least 100 non-zero values. The query I used is this:
select *
from (
select sensor_id, timestamp, "value",
sum(cast("value" > 0 as INT)) over w as cum_nonzero_measurements,
row_number() over w as cum_measurements,
first_value(timestamp) over w - timestamp as age
from measurements
window w as (partition by sensor_id order by timestamp desc)
) windowed
where (cum_nonzero_measurements <= 100 or age < interval '2 weeks' )
and sensor_id in ($1)
order by sensor_id, timestamp desc
This query only needs the most recent N measurements for a sensor, although N can vary. The smart way to execute such a query would be to start reading from the most recent value going backward in time. However Postgresql doesn't realize the query can be executed in that way, and insists on loading all measurements for the given sensor_id
s, doing a window aggregation over all rows, and only then filtering out most of the rows to get the result.
I tried with different indices, which helps it retrieve all rows for the requested sensors more quickly, but whatever I do Postgres keeps loading all rows for the sensors in question. Currently the performance is acceptable, but that doesn't scale very well if the dataset grows.
Is there any way to convince Postgres that it doesn't need to load all the rows, only the most recent ones?
WHERE
condition inside the subquery. You won't get your window function for the first few rows though.sensor_id
condition, but Postgres already pushes that down to the subquery by itself.extract(epoch from age) < 60*60*24*14 /*two weeks*/
can be rewritten asage < INTERVAL '2 weeks'