跳至主要內容

3.5 窗口函数

LiuSongLing大约 3 分钟pgsqlpgsql

窗口函数对一组与当前行以某种方式相关的表行执行计算。

这与可以使用 aggregate 函数完成的计算类型相当。

但是,窗口函数不会像非窗口聚合调用那样导致行分组为单个输出行。

相反,这些行保留其单独的身份。在后台,window 函数能够访问的不仅仅是查询结果的当前行。

以下示例显示了如何将每个员工的薪水与其部门的平均薪水进行比较:

SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;

窗口函数调用始终在窗口函数的名称和参数后面直接包含一个 OVER 子句。这就是它在语法上与普通函数或非窗口聚合的区别。

OVER 子句准确确定如何拆分查询的行以供 window 函数处理。OVER 中的 PARTITION BY 子句将行划分为多个组或分区,这些组或分区共享 PARTITION BY 表达式的相同值。

对于每一行,窗口函数是在与当前行属于同一分区的行之间计算的。




您还可以使用 OVER 中的 ORDER BY 控制窗口函数处理行的顺序。(窗口 ORDER BY 甚至不必匹配行的输出顺序.)下面是一个示例:

SELECT depname, empno, salary,
       rank() OVER (PARTITION BY depname ORDER BY salary DESC)
FROM empsalary;

如上所示,rank 函数使用 ORDER BY 子句定义的顺序为当前行分区中的每个不同 ORDER BY 值生成数字排名。

rank 不需要显式参数,因为它的行为完全由 OVER 子句决定。

窗口函数考虑的行是由查询的 FROM 子句生成的“虚拟表”的行,一个查询可以包含多个窗口函数,这些窗口函数使用不同的 OVER 子句以不同的方式对数据进行切片,但它们都作用于此虚拟表定义的同一行集合。

如果 over 子句后没有提供切片的数据范围,默认以整表作为切片数据范围:

SELECT salary, sum(salary) OVER () FROM empsalary;

但是如果我们添加 ORDER BY 子句,我们会得到非常不同的结果:

SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;

由于缺少 PARTITION BY 是整个表; 换句话说,每个 sum 都接管了整个表。

又因为提供了 ORDER BY,所以 sum 聚合的是 整个表分区开始到结果行的当前行的所有行。

如果在执行窗口计算后需要筛选或分组行,则可以使用子选择。例如:

SELECT depname, empno, salary, enroll_date
FROM
  (SELECT depname, empno, salary, enroll_date,
          rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
     FROM empsalary
  ) AS ss
WHERE pos < 3;




当查询涉及多个窗口函数时,可以使用单独的 OVER 子句写出每个窗口函数,但如果多个函数需要相同的窗口行为,则这是重复且容易出错的。

相反,每个窗口行为都可以在 WINDOW 子句中命名,然后在 OVER 中引用。例如:

SELECT sum(salary) OVER w, avg(salary) OVER w
  FROM empsalary
  WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);