This is a follow-up to this question, where my query was improved to use window functions instead of aggregates inside a LATERAL
join. While the query is now much faster, I've found that the results are not correct.
I need to perform computations on x year trailing time frames. For example, price_to_maximum_earnings
is computed per row by getting max(earnings)
over ten years ago to the current row, and dividing price
by the result. We'll use 1 year for simplicity here.
SQL Fiddle for this question. (Postgres 9.6)
As a simple example, price
and peak_earnings
for 2010-01-01
could be computed separately like this:
SELECT price
FROM security_data
WHERE date = '2010-01-01'
AND security_id = 'SPX';
SELECT max(earnings) AS min_earnings
FROM bloomberg.security_data
WHERE date >= '2000-01-01'
AND date <= '2010-01-01'
AND security_id = 'SPX';
To do this per row, I use the following:
SELECT security_id, date, price
, CASE WHEN date1 >= min_date
THEN price / NULLIF(max(earnings) FILTER (WHERE date >= date1) OVER w, 0) END AS price_to_peak_earnings
FROM
(
SELECT record_id, security_id, price, date, earnings
, (date - interval '1 y')::date AS date1
, min(date) OVER (PARTITION BY security_id) AS min_date
FROM security_data
) d
WINDOW w AS (PARTITION BY security_id);
I believe the issue here stems from the use of FILTER
, as it doesn't seem to be working as I want it to. Note that in the linked SQL Fiddle, I've displayed the result of the FILTER
, and for each row the peak_earnings
and minimum_earnings
are just the max and min for the entire data set. They should be the max/min values of earnings
from 1 year ago to the current row.
What's going on here? I know from the answer to this question that I can't simply say FILTER (WHERE date >= date1 AND date <= current_row.date)
, so is there a solution I'm missing? I cannot use window frames because I have an uncertain number of rows for any given time frame, so I couldn't just say OVER (ROWS BETWEEN 365 PRECEDING AND CURRENT ROW)
. Can I use a frame and a filter? That might get over a year previous, then the filter could catch every invalid date. I've tried this but have not been successful.