负荷峰谷差值指日最大负荷与日最小负荷之差,即$ p{\text{max}} - p{\text{min}} $;单纯用max()/min()聚合会丢失时间点、分组上下文及并列峰值等行级信息,需用窗口函数(如max() over、rank())按站点和日期分区计算并保留原始粒度。

什么是负荷峰谷差值,为什么不能直接用 MAX/MIN?
负荷峰谷差值 = 日最大负荷 − 日最小负荷,表面看用 MAX() 和 MIN() 聚合就能算。但实际业务中,你往往需要:保留原始时间点、按变电站/线路分组对比、叠加滚动周期(如连续7天峰谷差趋势)、或标记“是否为当日峰值时刻”。这时候单纯聚合会丢失行级上下文——窗口函数正是为此而生。
用 MAX() OVER () 和 MIN() OVER () 计算日级峰谷差
关键在于分区与排序逻辑必须对齐业务粒度。电力负荷数据通常含 station_id、load_time(精确到分钟)、active_power 字段。按天+站点分组是常见起点:
SELECT
station_id,
DATE(load_time) AS load_date,
load_time,
active_power,
MAX(active_power) OVER (
PARTITION BY station_id, DATE(load_time)
) AS daily_peak,
MIN(active_power) OVER (
PARTITION BY station_id, DATE(load_time)
) AS daily_valley,
MAX(active_power) OVER (
PARTITION BY station_id, DATE(load_time)
) - MIN(active_power) OVER (
PARTITION BY station_id, DATE(load_time)
) AS peak_valley_diff
FROM load_data
WHERE load_time >= '2024-01-01';
- 务必确认
DATE(load_time)在你的数据库中是否支持(PostgreSQL 用DATE(),MySQL 用DATE(),Oracle 需用TRUNC(load_time)) - 如果负荷数据存在空值,
MAX()/MIN()会自动忽略,但若整日全为 NULL,则该日结果为空——需结合COUNT()或COALESCE()补缺 - 不要在同一个
OVER子句里混用不同PARTITION BY,否则语法报错:ERROR: window definition contains multiple partition clauses
识别“峰值时刻”并关联设备状态——需要 RANK() 而非 ROW_NUMBER()
当某日出现多个相同最大负荷值(比如两个15分钟间隔都测得 245.6 MW),你可能需要全部标出,而非只取第一个。此时 RANK() 比 ROW_NUMBER() 更合理:
SELECT
station_id,
load_time,
active_power,
RANK() OVER (
PARTITION BY station_id, DATE(load_time)
ORDER BY active_power DESC
) AS peak_rank
FROM load_data;
-
RANK()对相同值赋予相同排名(如 1,1,3),ROW_NUMBER()则强制唯一(1,2,3) - 若后续要 JOIN 设备台账表查开关状态,必须用
RANK() = 1过滤,否则漏掉并列峰值 - ORDER BY 后不可加
NULLS LAST(除非明确知道 NULL 表示异常缺失,且你希望它排末尾);多数电力系统把 NULL 当无效采样,应提前用WHERE active_power IS NOT NULL过滤
滚动7天峰谷差趋势——ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 的陷阱
滚动窗口适合观察峰谷差变化率,但电力数据按时间戳排序时,必须确保每站每天有且仅有一条代表值(比如取日最大负荷),否则 ROWS 模式会按物理行数滑动,而非按自然日滑动:
- 先聚合出每日峰值:用
GROUP BY station_id, DATE(load_time)得到daily_max表 - 再在其上开窗:
AVG(peak_valley_diff) OVER (PARTITION BY station_id ORDER BY load_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) - 若原始表每分钟一条,直接在上面套
ROWS BETWEEN 6 PRECEDING,很可能把“同一日的60条记录”全扫进来,导致峰谷差被错误放大 - PostgreSQL 支持
RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW,但 MySQL 8.0 不支持INTERVAL在窗口中使用,必须预处理日期序列
真正难的不是写法,而是确认“7天”指日历日还是工作日、是否跳过检修停运日——这些业务规则必须在窗口外处理,窗口函数本身不理解业务语义。











