PostgreSQL:窗口函数

聚合函数将结果集进行计算并且通常返回一行。窗口函数也是基于结果集的运算。与聚合函数不同的是,窗口函数并不会将结果集进行分组合并输出一行;而是将计算的结果合并到基于结果集运算的列上。

语法

function_name ([expression [, expression ...]]) [FILTER (WHERE filter_clause)]
    OVER (window_definition)
    
window_definition:
  [existing_window_name]
  [PARTITION BY expression [, ...]]
  [ORDER BY expression [ASC | DESC | USING operator] [NULLS {FIRST | LAST }] [, ...]]
  [frame_clause]
  • OVER:表示窗口函数的关键字

  • PARTITION BY:对查询返回的结果集进行分组,之后窗口函数处理分组的数据。

  • ORDER BY:设定结果集的分组数据排序

测试数据创建

-- 创建成绩表
create table score(
id serial PRIMARY key,
subject   character(32),
stu_name  character(32),
grade     NUMERIC(3,0)
);
 
-- 插入数据
INSERT INTO SCORE(subject,stu_name,grade) values 
('语文','小王',80),
('语文','小张',70),
('语文','小李',80),
('英语','小王',90),
('英语','小张',70),
('英语','小李',50),
('数学','小王',100),
('数学','小张',70),
('数学','小李',65);

avg() OVER()

聚合函数后接 over属性的窗口函数表示在一个查询结果集上应用聚合函数。

查询没名学生学习成绩并且显示课程的平均分:

-- 对比
-- 先算课程的平均分,再用 score 表与平均分表关联查询
SELECT s.stu_name, s.subject, s.grade, v.avgscore
  FROM score s
  LEFT JOIN (
    SELECT subject, avg(grade) AS avgscore
      FROM score
     GROUP BY subject
  ) v ON s.subject = v.subject
 ORDER BY s.stu_name, s.subject
 
-- 使用窗口函数
SELECT s.stu_name, s.subject, grade,
       AVG(grade) OVER (PARTITION BY subject) AS avgscore
  FROM score s
 ORDER BY s.stu_name, s.subject

row_number()

对结果集分组后的数据标注行号,从 1 开始

SELECT row_number() OVER (PARTITION BY subject ORDER BY score desc), *
  FROM score;
  
SELECT row_number() OVER (ORDER BY score desc) as rownum, *
  FROM score;

rank()

当组内某行字段值相同时,行号重复并且行号产生间隙

SELECT rank() OVER (PARTITION BY subject ORDER BY score desc), *
  FROM score;

dense_rank()

当组内某行字段值相同时,虽然行号重复,但行号不产生间隙

SELECT dense_rank() OVER (PARTITION BY subject ORDER BY score desc), *
  FROM score;

lag()

获取行偏移 offset 那行某个字段的数据,语法如下:

lag(value anyelement [, offset integer [, default anyelement]])
  • value:指定要返回记录的字段。

  • offset:指行偏移量,可以是正整数或负整数,正整数表示取值结果集中向上偏移的记录,负整数表示取结果集中向下偏移的记录,默认值为 1。

  • default:指如果不存在 offset 偏移的行时用默认值填充,default 值默认为 null。

获取向上偏移 1 行记录的 id 值

SELECT lag(id, 1) OVER (PARTITION BY subject ORDER BY score desc), *
  FROM score;

first_value()

用来取结果集每一个分组的第一行数据的字段值

score 表按课程分组后取分组的第一行的分数

SELECT first_value(score) OVER (PARTITION BY subject ORDER BY score desc), *
  FROM score;

last_value()

用来取结果集每一个分组的最后一行数据的字段值

score 表按课程分组后取分组的最后一行的分数

SELECT last_value(score) OVER (PARTITION BY subject ORDER BY score desc), *
  FROM score;

nth_value()

用来取结果集每一个分组的指定行数据的字段值

last_value(value any, nth integer)
  • value:指定表的字段

  • nth:指定结果集分组数据中的第几行,如果不存在则返回空

score 表按课程分组后取分组的第二行的分数

SELECT nth_value(score, 2) OVER (PARTITION BY subject ORDER BY score desc), *
  FROM score;

别名

如果 SQL 中需要多次使用窗口函数,可以使用窗口函数别名:

SELECT ... FROM ... WINDOW window_name AS ( window_definition )[, ...]
SELECT avg(score) OVER(r), sum(score) OVER(r), *
  FROM score
 WINDOW r as (PARTITION BY subject ORDER BY score desc)
请使用浏览器的分享功能分享到微信等