这是this问题的后续问题,其中我的查询得到了改进,以便在LATERAL
联接中使用窗口函数而不是聚合。虽然查询现在要快得多,但我发现结果不正确。
我需要在x年尾随时间帧上执行计算。例如,通过在十年前将price_to_maximum_earnings
提取到当前行并将max(earnings)
除以结果,每行计算price
。我们将在这里使用1年简化。
SQL Fiddle这个问题。 (Postgres 9.6)
举个简单的例子,price
的{{1}}和peak_earnings
可以像这样单独计算:
2010-01-01
要执行每行,我使用以下内容:
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';
我认为这里的问题源于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);
的使用,因为它似乎没有像我希望的那样工作。请注意,在链接的SQL小提琴中,我显示了FILTER
的结果,对于每一行,FILTER
和peak_earnings
只是整个数据集的最大值和最小值。它们应该是{1}从1年前到当前行的最大/最小值。
这里发生了什么?我从this问题的答案中知道我不能简单地说minimum_earnings
,那么我是否有一个解决方案?我无法使用窗口框架,因为我在任何给定的时间范围内都有不确定的行数,所以我不能只说earnings
。我可以使用框架和过滤器吗?这可能超过一年前,然后过滤器可以捕获每个无效的日期。我试过这个,但没有成功。
答案 0 :(得分:1)
我可以使用框架和过滤器吗?
你可以。但要么有限制:
FILTER
子句中的表达式只能看到获取值的相应行。无法引用窗口函数计算值的行。所以我没有看到根据那个行来制定过滤器的方法,除非我们进行庞大,昂贵的交叉连接 - 同一行用于许多不同的计算。或者我们回到可以引用父行的LATERAL
个子查询。
另一方面,帧定义根本不允许变量。它需要一个固定的数字,如您引用的相关答案中所述:
这些限制使您的特定查询难以实现。现在应该正确:
SELECT *
FROM (
SELECT record_id, security_id, date, price
, CASE WHEN do_calc THEN max(earnings) OVER w1 END AS peak_earnings
, CASE WHEN do_calc THEN min(earnings) OVER w1 END AS minimum_earnings
, CASE WHEN do_calc THEN price / NULLIF(max(earnings) OVER w1, 0) END AS price_to_peak_earnings
, CASE WHEN do_calc THEN price / NULLIF(min(earnings) OVER w1, 0) END AS price_to_minimum_earnings
FROM (
SELECT *, (date - 365) >= min_date AND s.record_id IS NOT NULL AS do_calc
FROM (
SELECT security_id, min_date
, generate_series(min_date, max_date, interval '1 day')::date AS date
FROM (
SELECT security_id, min(date) AS min_date, max(date) AS max_date
FROM security_data
GROUP BY 1
) minmax
) d
LEFT JOIN security_data s USING (security_id, date)
) sub1
WINDOW w1 AS (PARTITION BY security_id ORDER BY date ROWS BETWEEN 365 PRECEDING AND 1 PRECEDING)
) sub2
WHERE record_id IS NOT NULL
ORDER BY 1, 2;
问题中没有任何内容表明每个security_id
在同一天都有行。在子查询security_id
中计算每minmax
的最小/最大日期为我们提供了最短的时间范围。
计算的时间范围恰好是行当前日期之前的365天,不是,包括当前行(ROWS BETWEEN 365 PRECEDING AND 1 PRECEDING
)。 从聚合中排除当前行以与当前行进行比较通常更有用。
我将计算条件调整到相同的时间范围,以避免角落奇怪:(date - 365) >= min_date
在fiddle中,您在1月1日每增加1行,您可以看到与固定数量365天形成鲜明对比的傻瓜效果。在leapyears(2001,2005,...)之后窗框是空的。
我正在使用所有子查询,这通常比CTE快一点。
可以肯定的是,我们需要在帧定义中包含ORDER BY
。我更新了你所链接的旧答案:
我使用w1
作为窗口名称,用于“1年”期间。您可以添加w2
等,每个日期可以有任意天数。如果你需要的话,你可以适应水稻。甚至可能会根据当前日期生成整个查询......