在 postgres 窗口框架中定义相对百分比框架限制

问题描述 投票:0回答:1

我有一个查询,试图查找一组服务地点的平均能耗,这些服务地点在特定月份内每个服务地点的能耗差异在 ±5% 之内。我知道 range 和 rows 函数无法定义特定的数字范围,而是使用前面和后面的子句,但是我如何编写这个查询来满足我的需要?

WITH energy_consumption_cte AS (
    SELECT
        sl.id,
        sl.sqft,
        SUM(CASE WHEN dl.log_label = 'energy use' THEN dl.value END) AS monthly_consumption
    FROM
        serv_location sl
    INNER JOIN
        device d ON d.service_loc_id = sl.id
    INNER JOIN
        device_log dl ON dl.device_id = d.id
    WHERE
        dl."timestamp" >= '2022-08-01 00:01:00.000'::timestamp
        AND dl."timestamp" < '2022-09-01 00:01:00.000'::timestamp
        AND sl.sqft IS NOT NULL
    GROUP BY
        sl.id, sl.sqft
)

SELECT
    id,
    sqft,
    monthly_consumption,
    (monthly_consumption / AVG(monthly_consumption) OVER w) * 100 
    AS average_monthly_for_sqft_range
FROM
    energy_consumption_cte
window w as (PARTITION BY sqft ORDER BY sqft range between 
    ROUND(sqft * 0.95) and ROUND(sqft * 1.05))
ORDER BY
    sqft ASC;
sql postgresql aggregate-functions window-functions
1个回答
1
投票

窗框

range
模式可以定义特定范围。要使用包含与当前记录相比向上/向下 5% 的记录值的聚合函数,您应该想到
range between 0.05*sqft preceding and 0.05*sqft following
。问题是,你不能在那里使用变量。

offset

PRECEDING
offset
FOLLOWING
框架选项中,offset 必须是不包含任何变量、聚合函数或窗口函数的表达式。

如果您尝试引用

sqft
列/字段,您将得到:

ERROR:  argument of RANGE must not contain variables
LINE 27:              RANGE BETWEEN sqft*0.05 PRECEDING
                                    ^

您可以通过首先使用

lag()
lead()
计算行与行之间的百分比变化来解决该限制,然后将其收集到步进
sum()
中,以便它保持
sqft
的初始顺序。一旦你有了这个,你可以在框架子句中使用常量偏移来达到 5% 来回。 db<>fiddle 的演示:

WITH energy_consumption_cte AS (
    SELECT sl.id,
           sl.sqft,
           SUM(dl.value)filter(where dl.log_label='energy use') AS monthly_consumption
    FROM serv_location sl
    INNER JOIN device d ON d.service_loc_id = sl.id
    INNER JOIN device_log dl ON dl.device_id = d.id
    WHERE dl."timestamp" >= '2022-08-01 00:01:00.000'::timestamp
      AND dl."timestamp" < '2022-09-01 00:01:00.000'::timestamp
      AND sl.sqft IS NOT NULL
    GROUP BY sl.id, sl.sqft 
),square_footage_steps_cte AS (
    SELECT *, 1-lag(sqft,1,sqft)over(order by sqft)/sqft::numeric AS sqft_step
    FROM energy_consumption_cte
    ORDER BY sqft ASC
),square_footage_steps_cumulative_cte AS (
    SELECT *, sum(sqft_step)over(order by sqft) AS sqft_step_sum
    FROM square_footage_steps_cte
    ORDER BY sqft ASC)
SELECT id,
       sqft,
       monthly_consumption,
       AVG(monthly_consumption) OVER w AS average_monthly_for_sqft_range,
       array_agg(id)over w as rows_in_frame
FROM square_footage_steps_cumulative_cte 
WINDOW w as (ORDER BY sqft_step_sum
             RANGE BETWEEN 0.05 PRECEDING
                   AND     0.05 FOLLOWING)
ORDER BY sqft ASC;
id 平方英尺 每月_消费 平方英尺范围的平均每月 帧内行数
1 100 400 700.0000000000000000 {1,2}
2 105 1000 1000.0000000000000000 {1,2,3}
3 110 1600 1600.0000000000000000 {2,3,4}
4 115 2200 1900.0000000000000000 {3,4}
5 200 2800 3100.0000000000000000 {5,6}
6 210 3400 3100.0000000000000000 {5,6}
© www.soinside.com 2019 - 2024. All rights reserved.