sql窗口函数 over

over的功能是什么

@radix-vue

OVER 是 SQL 窗口函数的一个关键字,用于定义一个窗口或帧,窗口函数将在这个窗口或帧上进行计算操作。OVER 关键字与窗口函数一起使用,如 SUM()AVG()ROW_NUMBER()RANK(), 等等。

功能和用途

OVER 允许您在不缩减结果集的情况下,对结果集进行计算。这与传统的聚合函数(如 SUM()COUNT())不同,后者会将结果集缩减成较少的行数。

语法

 

sql

复制代码

<窗口函数> OVER (
  [PARTITION BY <partition_col1>, <partition_col2>, ...]
  [ORDER BY <order_col1> [ASC|DESC], <order_col2> [ASC|DESC], ...]
  [ROWS|RANGE <frame_clause>]
)

子句

  1. PARTITION BY

    • 将结果集按一个或多个列分区。
    • 每个分区内独立进行窗口函数计算。
    • 类似于 GROUP BY,但不缩减结果集的行数。
     

    sql

    复制代码

    SUM(salary) OVER (PARTITION BY department_id)
    
  2. ORDER BY

    • 指定分区内的行顺序,有时结合 PARTITION BY 使用。
    • 确定窗口函数的计算顺序。
     

    sql

    复制代码

    SUM(salary) OVER (PARTITION BY department_id ORDER BY hire_date)
    
  3. ROWS 或 RANGE

    • 定义计算窗口的具体范围或帧。
    • ROWS 指定确切的行数范围,例如当前行的前两行和当前行。
    • RANGE 基于值范围,例如当前行和与当前行值相等的所有其他行。
     

    sql

    复制代码

    SUM(salary) OVER (ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
    

示例和解释

基本示例:ROW_NUMBER()
 

sql

复制代码

SELECT 
    employee_id, 
    department_id, 
    salary, 
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num
FROM employees;

在这个查询中:

  • PARTITION BY department_id 将数据按部门分组。
  • 在每个部门内,ORDER BY salary DESC 按薪水从高到低排序。
  • ROW_NUMBER() 为每个分区内的每一行分配唯一的行号。
聚合函数示例:SUM()
 

sql

复制代码

SELECT 
    employee_id, 
    salary, 
    SUM(salary) OVER (PARTITION BY department_id ORDER BY hire_date) AS cumulative_salary
FROM employees;

在这个查询中:

  • PARTITION BY department_id 根据部门进行分区。
  • ORDER BY hire_date 指定部门内的排序规则。
  • SUM(salary) OVER (...) 计算每个部门内按雇佣日期排序的累计工资。
高级示例:移动平均
 

sql

复制代码

SELECT 
    order_id, 
    order_date, 
    amount, 
    AVG(amount) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM orders;

在这个查询中:

  • ORDER BY order_date 指定按订单日期排序。
  • ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 定义窗口帧为当前行及其前两行。
  • AVG(amount) OVER (...) 计算指定窗口内的移动平均值。

总结

OVER 子句使得窗口函数变得非常强大和灵活,它允许在不缩减结果集的情况下进行各种复杂的计算和分析操作。通过使用 PARTITION BYORDER BY 和窗口帧定义,可以对数据进行详细且复杂的分析,适用于诸如累积总和、移动平均、行排名等各种场景。

了解和掌握 OVER 子句的使用,将大大提升您在SQL数据查询和分析中的能力。

请给我举几个例子

@radix-vue

当然,下面是一些使用OVER子句的常见窗口函数的例子。

1. ROW_NUMBER()

示例:为每个部门的员工分配一个唯一的行号。
 

sql

复制代码

SELECT
    employee_id,
    department_id,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num
FROM employees;

解释

  • PARTITION BY department_id 将结果集按部门分区。
  • ORDER BY salary DESC 按薪水降序排列每个部门内的员工。
  • ROW_NUMBER() 为每个分区内的每一行分配唯一的行号。

2. RANK()

示例:为每个部门的员工分配排名,相同薪水的员工会有相同的排名。
 

sql

复制代码

SELECT
    employee_id,
    department_id,
    salary,
    RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank
FROM employees;

解释

  • PARTITION BY department_id 将结果集按部门分区。
  • ORDER BY salary DESC 按薪水降序排列每个部门内的员工。
  • RANK() 为每个分区内的每一行分配排名,薪水相同的员工会有相同的排名,下一跳会跳过这些相同排名的行数。

3. DENSE_RANK()

示例:为每个部门的员工分配排名,相同薪水的员工会有相同的排名,不会跳跃。
 

sql

复制代码

SELECT
    employee_id,
    department_id,
    salary,
    DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dense_rank
FROM employees;

解释

  • PARTITION BY department_id 将结果集按部门分区。
  • ORDER BY salary DESC 按薪水降序排列每个部门内的员工。
  • DENSE_RANK() 为每个分区内的每一行分配排名,薪水相同的员工会有相同的排名,且不会跳跃。

4. LAG()

示例:获取当前行员工的前一个员工的薪水(按雇佣日期排序)。
 

sql

复制代码

SELECT
    employee_id,
    hire_date,
    salary,
    LAG(salary, 1) OVER (ORDER BY hire_date) AS prev_salary
FROM employees;

解释

  • ORDER BY hire_date 按雇佣日期排序。
  • LAG(salary, 1) 获取上一行的薪水。

5. LEAD()

示例:获取当前行员工的下一个员工的薪水(按雇佣日期排序)。
 

sql

复制代码

SELECT
    employee_id,
    hire_date,
    salary,
    LEAD(salary, 1) OVER (ORDER BY hire_date) AS next_salary
FROM employees;

解释

  • ORDER BY hire_date 按雇佣日期排序。
  • LEAD(salary, 1) 获取下一行的薪水。

6. 聚合函数作为窗口函数

示例:计算每个部门每个员工的累计薪水。
 

sql

复制代码

SELECT
    employee_id,
    department_id,
    salary,
    SUM(salary) OVER (PARTITION BY department_id ORDER BY hire_date) AS cumulative_salary
FROM employees;

解释

  • PARTITION BY department_id 将结果集按部门分区。
  • ORDER BY hire_date 按雇佣日期排序。
  • SUM(salary) OVER (...) 计算每个分区内的累计薪水。

7. 移动平均

示例:计算过去三行的移动平均。
 

sql

复制代码

SELECT
    order_id,
    order_date,
    amount,
    AVG(amount) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM orders;

解释

  • ORDER BY order_date 按订单日期排序。
  • ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 确定窗口帧为当前行及其前两行。
  • AVG(amount) OVER (...) 计算在窗口帧内的移动平均值。

8. 使用 NTILE()

示例:将结果集按薪水分成4个等分的组,并分配一个组编号。
 

sql

复制代码

SELECT
    employee_id,
    salary,
    NTILE(4) OVER (ORDER BY salary DESC) AS salary_group
FROM employees;

解释

  • ORDER BY salary DESC 按薪水降序排列员工。
  • NTILE(4) 将结果集分成四等分,并为每行分配一个组编号。

这些示例展示了使用OVER子句的各种窗口函数的强大功能和灵活性。通过这些查询,您可以在不缩减结果集的情况下执行复杂的分析和计算。

能不能给我一个带数据表的例子

@radix-vue

当然可以。让我们看一个具体的数据表和相关的SQL查询示例,以便更好地理解窗口函数和 OVER 子句的用法。

示例数据表

假设我们有一个表 employees,其中包含以下数据:

employee_iddepartment_idhire_datesalary
112020-01-015000
212020-02-016000
312020-03-015500
422020-01-158000
522020-02-207000
622020-03-107200

示例1:ROW_NUMBER()

目的:为每个部门的员工分配一个唯一的行号。
 

sql

复制代码

SELECT
    employee_id,
    department_id,
    hire_date,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY hire_date) AS row_num
FROM employees;

结果:

employee_iddepartment_idhire_datesalaryrow_num
112020-01-0150001
212020-02-0160002
312020-03-0155003
422020-01-1580001
522020-02-2070002
622020-03-1072003

示例2:RANK()

目的:在每个部门中,根据薪水为员工分配排名,相同薪水的员工会有相同的排名。
 

sql

复制代码

SELECT
    employee_id,
    department_id,
    salary,
    RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank
FROM employees;

结果:

employee_iddepartment_idsalaryrank
2160001
3155002
1150003
4280001
6272002
5270003

示例3:LAG()

目的:获取当前行员工的前一个员工的薪水(按雇佣日期排序)。
 

sql

复制代码

SELECT
    employee_id,
    hire_date,
    salary,
    LAG(salary, 1) OVER (ORDER BY hire_date) AS prev_salary
FROM employees;

结果:

employee_idhire_datesalaryprev_salary
12020-01-015000NULL
42020-01-1580005000
22020-02-0160008000
52020-02-2070006000
32020-03-0155007000
62020-03-1072005500

示例4:SUM() OVER (PARTITION BY ...)

目的:计算每个部门每个员工的累计薪水。
 

sql

复制代码

SELECT
    employee_id,
    department_id,
    salary,
    SUM(salary) OVER (PARTITION BY department_id ORDER BY hire_date) AS cumulative_salary
FROM employees;

结果:

employee_iddepartment_idsalarycumulative_salary
1150005000
21600011000
31550016500
4280008000
52700015000
62720022200

示例5:移动平均

目的:计算过去两行的移动平均薪水。
 

sql

复制代码

SELECT
    employee_id,
    hire_date,
    salary,
    AVG(salary) OVER (ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM employees;

结果:

employee_idhire_datesalarymoving_avg
12020-01-0150005000.00
42020-01-1580006500.00
22020-02-0160006333.33
52020-02-2070007000.00
32020-03-0155006166.67
62020-03-1072006566.67

这些示例展示了如何使用窗口函数来执行复杂的分析和计算。通过这些查询,可以在不缩减结果集的情况下,对数据进行详细的分析。

### 复杂的SQL窗口函数 `OVER` 用法示例及解析 #### 使用场景一:多列分区并排序后的累计求和 当需要基于多个维度进行数据分析时,可以利用 `PARTITION BY` 和 `ORDER BY` 来实现更精细的操作。例如,在销售记录表中按地区和地区内的不同产品类别分别统计销售额,并计算每个类别的累计金额。 ```sql SELECT region, product_category, sale_amount, SUM(sale_amount) OVER (PARTITION BY region, product_category ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_sales FROM sales_table; ``` 此查询语句会按照区域和地区内不同的商品种类来划分数据集[^1],并对每一部分中的日期顺序排列后执行累加操作,从而得到截至当前行为止的所有历史交易总额。 #### 场景二:获取前后若干条记录的信息 通过指定 `ROWS/RANGE BETWEEN ... PRECEDING/FOLLOWING` 可以访问相邻几行的数据项。这对于时间序列预测模型构建特别有用,因为能够方便地取得过去几天甚至未来一段时间的趋势变化情况作为特征输入给机器学习算法训练使用。 假设有一个股票价格日志表格,想要知道每一天收盘价相对于前一天的变化幅度: ```sql WITH stock_prices AS ( SELECT '2023-09-01'::date as trade_date, 100.5 as close_price UNION ALL SELECT '2023-09-02', 101.7 UNION ALL ... ) SELECT trade_date, close_price, LAG(close_price, 1) OVER (ORDER BY trade_date ASC) prev_close_price, ROUND((close_price - LAG(close_price, 1) OVER (ORDER BY trade_date ASC)) / LAG(close_price, 1) * 100, 2) change_percent FROM stock_prices; ``` 这里定义了一个CTE表达式模拟了一些简单的股价变动;主查询则应用了 `LAG()` 函数去检索上一日的价格,并据此算出了百分比增减量[^2]。 #### 场景三:动态调整窗口大小 有时可能希望随着遍历过程改变所考虑的历史长度范围。这可以通过设置可变边界条件达成目的。比如下面的例子展示了如何在一个订单明细表里找出最近三个月以来每个月份最高销量的产品型号及其对应的月度总销量: ```sql SELECT DISTINCT ON (month) month, model_id, MAX(total_sold) OVER w max_monthly_sales, total_sold monthly_sales FROM orders_detail WINDOW w AS (DISTRIBUTION BY model_id PARTITION BY EXTRACT(MONTH FROM order_time), EXTRACT(YEAR FROM order_time) ORDER BY total_sold DESC RANGE BETWEEN INTERVAL '3 months' PRECEDING AND CURRENT ROW); ``` 这段代码片段首先创建了一个名为 `w` 的窗口框架,它根据年份月份组合以及具体机型来进行分组聚合运算,同时指定了一个相对固定的滚动周期——即从当前时刻往前推移三个自然月的时间跨度内寻找最大值[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值