简体   繁体   中英

Speed up postgres read window query on overlapping date ranges

I have a table (simplified) that contains readings like follows

meter_id     read_date        value
1            2017-01-01         10
1            2017-01-15         15
1            2017-02-05         20
1            2017-04-15         22
2            2016-12-14         120
2            2016-03-02         200

This table contains millions of readings.

And I have a view (or query) that goes something like

 select  meter_id, read_date as start_read_date, value as start_value, 
  CASE
      WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
      ELSE lead(.read_date) OVER read_wdw::date
  END::date AS read_end_date,
  lead(value) OVER read_wdw AS end_value,
 from reads_table 
 
  WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date);

I need to be able to query dates within a certain month. So start_read_date, end_read_date between eg '2017-01-01' and '2017-01-31'

So eg

select * from my_view where daterange(start_read_date,end_read_date, '[]') && daterange('2017-01-01', '2017-01-31', '[]) 



Which with the above table would return 
    meter_id  start_read_date   start_value  end_read_date  end_value
    1            2017-01-01         10        2017-01-15      15
    1            2017-01-15         15        2017-02-05      20 
    2            2016-12-14         120       2016-03-02      200

Is there a way to do a similar query on this table without having to build the whole view first to get my desired result?

Something like (which doesn't work)

 select  meter_id, read_date as start_read_date, value as start_value, 
  CASE
      WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
      ELSE lead(.read_date) OVER read_wdw::date
  END::date AS read_end_date,
  lead(value) OVER read_wdw AS end_value,
 from reads_table 
 where read_date between '2017-01-01' and '2017-01-31'
or lead(read_date) over read_window between '2017-01-01' and '2017-01-31'
  WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date);

Actually wrapping it in another select seems to resolve...

select * from (

select  meter_id, read_date as start_read_date, value as start_value, 
  CASE
      WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
      ELSE lead(.read_date) OVER read_wdw::date
  END::date AS read_end_date,
  lead(value) OVER read_wdw AS end_value,
 from reads_table 
 
  WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date)
)sub 

where read_start_date between ...
or read_end_date between ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM