sql - over()的用法

本文通过多个实战案例详细解析了 Oracle 数据库中 OVER 函数的应用,包括如何进行连续求和、部门内连续求和及综合求和等操作,并展示了不同 ORDER BY 和 PARTITION BY 子句组合的效果。
oracle over函数 详解(转) 
SQL> select deptno,ename,sal
   2   from emp
   3   order by deptno;

DEPTNO ENAME          SAL
---------- ---------- ----------
       10 CLARK          2450
          KING          5000
          MILLER           1300

       20 SMITH          800
          ADAMS          1100
          FORD          3000
          SCOTT          3000
          JONES          2975

       30 ALLEN          1600
          BLAKE          2850
          MARTIN           1250
          JAMES          950
          TURNER           1500
          WARD          1250


已选择14行。



2.先来一个简单的,注意over(...)条件的不同,
使用 sum(sal) over (order by ename)... 查询员工的薪水“连续”求和,
注意over (order   by ename)如果没有order by 子句,求和就不是“连续”的,
放在一起,体会一下不同之处:

SQL> select deptno,ename,sal,
   2   sum(sal) over (order by ename) 连续求和,
   3   sum(sal) over () 总和,                -- 此处sum(sal) over () 等同于sum(sal)
   4   100*round(sal/sum(sal) over (),4) "份额(%)"
   5   from emp
   6   /

DEPTNO ENAME          SAL 连续求和    总和 份额(%)
---------- ---------- ---------- ---------- ---------- ----------
       20 ADAMS          1100    1100    29025    3.79
       30 ALLEN          1600    2700    29025    5.51
       30 BLAKE          2850    5550    29025    9.82
       10 CLARK          2450    8000    29025    8.44
       20 FORD          3000    11000    29025    10.34
       30 JAMES          950    11950    29025    3.27
       20 JONES          2975    14925    29025    10.25
       10 KING          5000    19925    29025    17.23
       30 MARTIN           1250    21175    29025    4.31
       10 MILLER           1300    22475    29025    4.48
       20 SCOTT          3000    25475    29025    10.34
       20 SMITH          800    26275    29025    2.76
       30 TURNER           1500    27775    29025    5.17
       30 WARD          1250    29025    29025    4.31

已选择14行。


3.使用子分区查出各部门薪水连续的总和。注意按部门分区。注意over(...)条件的不同,
sum(sal) over (partition by deptno order by ename) 按部门“连续”求总和
sum(sal) over (partition by deptno) 按部门求总和
sum(sal) over (order by deptno,ename) 不按部门“连续”求总和
sum(sal) over () 不按部门,求所有员工总和,效果等同于sum(sal)。

SQL> select deptno,ename,sal,
   2   sum(sal) over (partition by deptno order by ename) 部门连续求和,--各部门的薪水"连续"求和
   3   sum(sal) over (partition by deptno) 部门总和,   -- 部门统计的总和,同一部门总和不变
   4   100*round(sal/sum(sal) over (partition by deptno),4) "部门份额(%)",
   5   sum(sal) over (order by deptno,ename) 连续求和, --所有部门的薪水"连续"求和
   6   sum(sal) over () 总和,   -- 此处sum(sal) over () 等同于sum(sal),所有员工的薪水总和
   7   100*round(sal/sum(sal) over (),4) "总份额(%)"
   8   from emp
   9   /

DEPTNO ENAME SAL 部门连续求和 部门总和 部门份额(%) 连续求和 总和   总份额(%)
------ ------ ----- ------------ ---------- ----------- ---------- ------ ----------
10            CLARK 2450      2450     8750       28    2450   29025    8.44
                KING      5000      7450    8750    57.14    7450   29025    17.23
                MILLER 1300       8750    8750    14.86    8750   29025    4.48

20           ADAMS 1100       1100    10875    10.11    9850   29025    3.79
                FORD    3000       4100    10875    27.59    12850   29025    10.34
               JONES 2975       7075    10875    27.36    15825   29025    10.25
               SCOTT 3000       10075   10875    27.59    18825   29025    10.34
                SMITH   800        10875   10875        7.36    19625   29025    2.76

30           ALLEN 1600       1600    9400    17.02    21225   29025    5.51
                BLAKE 2850       4450    9400    30.32    24075   29025    9.82
               JAMES 950         5400    9400    10.11    25025   29025    3.27
               MARTIN 1250       6650    9400        13.3    26275   29025    4.31
                TURNER1500       8150    9400    15.96    27775   29025    5.17
               WARD    1250       9400    9400        13.3    29025   29025    4.31


已选择14行。



4.来一个综合的例子,求和规则有按部门分区的,有不分区的例子
SQL> select deptno,ename,sal,sum(sal) over (partition by deptno order by sal) dept_sum,
   2   sum(sal) over (order by deptno,sal) sum
   3   from emp;

DEPTNO ENAME          SAL DEPT_SUM        SUM
---------- ---------- ---------- ---------- ----------
       10 MILLER           1300    1300    1300
          CLARK          2450    3750    3750
          KING          5000    8750    8750

       20 SMITH          800        800    9550
          ADAMS          1100    1900    10650
          JONES          2975    4875    13625
          SCOTT          3000    10875    19625
          FORD          3000    10875    19625

       30 JAMES          950        950    20575
          WARD          1250    3450    23075
          MARTIN           1250    3450    23075
          TURNER           1500    4950    24575
          ALLEN          1600    6550    26175
          BLAKE          2850    9400    29025


已选择14行。



5.来一个逆序的,即部门从大到小排列,部门里各员工的薪水从高到低排列,累计和的规则不变。

SQL> select deptno,ename,sal,
   2   sum(sal) over (partition by deptno order by deptno desc,sal desc) dept_sum,
   3   sum(sal) over (order by deptno desc,sal desc) sum
   4   from emp;

DEPTNO ENAME          SAL DEPT_SUM        SUM
---------- ---------- ---------- ---------- ----------
       30 BLAKE          2850    2850    2850
          ALLEN          1600    4450    4450
          TURNER           1500    5950    5950
          WARD          1250    8450    8450
          MARTIN           1250    8450    8450
          JAMES          950    9400    9400

       20 SCOTT          3000    6000    15400
          FORD          3000    6000    15400
          JONES          2975    8975    18375
          ADAMS          1100    10075    19475
          SMITH          800    10875    20275

       10 KING          5000    5000    25275
          CLARK          2450    7450    27725
          MILLER           1300    8750    29025


已选择14行。



6.体会:在"... from emp;"后面不要加order   by 子句,使用的分析函数的(partition by deptno order by sal)
里已经有排序的语句了,如果再在句尾添加排序子句,一致倒罢了,不一致,结果就令人费劲了。如:

SQL> select deptno,ename,sal,sum(sal) over (partition by deptno order by sal) dept_sum,
   2   sum(sal) over (order by deptno,sal) sum
   3   from emp
   4   order by deptno desc;

DEPTNO ENAME          SAL DEPT_SUM        SUM
---------- ---------- ---------- ---------- ----------
       30 JAMES          950        950    20575
          WARD          1250    3450    23075
          MARTIN           1250    3450    23075
          TURNER           1500    4950    24575
          ALLEN          1600    6550    26175
          BLAKE          2850    9400    29025

       20 SMITH          800        800    9550
          ADAMS          1100    1900    10650
          JONES          2975    4875    13625
          SCOTT          3000    10875    19625
          FORD          3000    10875    19625

       10 MILLER           1300    1300    1300
          CLARK          2450    3750    3750
          KING          5000    8750    8750


已选择14行 

 

<think> 我们正在讨论SQL中的OVER关键字。根据引用内容,OVER关键字通常用于开窗函数(Window Functions),它定义了函数计算的数据窗口或分区。 引用中提到的内容: 引用[1]:OracleOVER用法,提到了rows between unbounded preceding and unbounded following,即窗口从第一行到最后一行。 引用[2]:开窗函数的OVER关键字后可以使用PARTITION BY子句定义分区,与GROUP BY不同,它不影响结果集的行数,只是为聚合计算提供分区。 引用[3]:示例中使用了row_number() over(partition by orgid order by userid),表示按orgid分区,然后在分区内按userid排序生成行号。 引用[4]:展示了在SQL Server中使用OVER(PARTITION BY ...)进行聚合计算,同时保留了原始行。 因此,OVER关键字的用法总结如下: 1. 基本作用:OVER()用于定义一个窗口(一组行),窗口函数在这个窗口上执行计算。窗口函数不会将多行合并为一行,而是为每一行返回一个值(比如聚合值、排名等)。 2. 语法结构: 窗口函数 OVER ( [PARTITION BY 列1, 列2, ...] -- 将数据分成多个分区(组),函数在每个分区内独立计算 [ORDER BY 列1 [ASC|DESC], ...] -- 指定分区内的排序方式 [ROWS/RANGE 子句] -- 定义窗口的起始和结束边界(框架) ) 3. 窗口函数类型: - 聚合函数:如SUM(), AVG(), COUNT(), MAX(), MIN()等。 - 排名函数:如ROW_NUMBER(), RANK(), DENSE_RANK(), NTILE()等。 - 分析函数:如LEAD(), LAG(), FIRST_VALUE(), LAST_VALUE()等。 4. 窗口框架(ROWS/RANGE): 用来进一步限制窗口的范围,常见的选项有: - ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: 从分区第一行到当前行(常用作累计计算) - ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING: 整个分区 - ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING: 当前行前一行到后一行 5. 注意: -OVER()内为空,则窗口为整个查询结果集。 - 当只有ORDER BY,没有ROWS/RANGE子句时,默认窗口为:RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(注意:这与ROWS的行为略有不同,RANGE是基于排序值范围,ROWS是物理行数)。 举例说明: 示例1:计算每个订单中每个产品的数量,同时显示订单总数量(每个订单的总数量在每一行重复) SELECT SalesOrderID, ProductID, OrderQty, SUM(OrderQty) OVER (PARTITION BY SalesOrderID) AS TotalOrderQty FROM Sales.SalesOrderDetail; 示例2:计算每个订单中每个产品的数量占比 SELECT SalesOrderID, ProductID, OrderQty, OrderQty * 1.0 / SUM(OrderQty) OVER (PARTITION BY SalesOrderID) AS Ratio FROM Sales.SalesOrderDetail; 示例3:按部门分区,按薪水排序,给每个部门的员工按薪水排名 SELECT department_id, employee_id, salary, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_salary_rank FROM employees; 示例4:计算累计薪水(按员工ID排序,计算从第一个员工到当前员工的薪水累加) SELECT employee_id, hire_date, salary, SUM(salary) OVER (ORDER BY employee_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_salary FROM employees; 示例5:移动平均(计算当前行及前两行的平均) SELECT date, sales, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; 注意事项: - 不同数据库对窗口函数的支持略有不同,但基本语法一致(Oracle, SQL Server, PostgreSQL, MySQL 8.0+等都支持)。 - ROWS和RANGE的区别:ROWS是按物理行(行位置),RANGE是按逻辑值(排序字段的值)。例如,如果排序字段有重复值,使用RANGE时,相同的值会被视为同一框架。 因此,OVER关键字使得我们可以进行复杂的分组计算,同时保留原始行的细节,这是它比GROUP BY更灵活的地方。 生成相关问题: 1. OVER子句中PARTITION BY和GROUP BY的区别? 2. ROWS和RANGE在窗口函数中的区别? 3. 如何利用OVER子句实现累计求和? 4. 开窗函数中ORDER BY的作用是什么? 5. 在MySQL中如何使用窗口函数? </think> ### SQL中`OVER()`关键字的用法详解 `OVER()`是SQL中的**窗口函数(Window Function)**核心语法,用于在特定数据窗口上执行计算而不聚合结果集。它保留原始行细节,同时支持分组计算、排序和动态分区。以下是核心用法--- #### 一、基础语法结构 ```sql 函数() OVER ( [PARTITION BY 列1, 列2, ...] -- 将数据分组(类似GROUP BY但保留所有行) [ORDER BY 列 [ASC|DESC]] -- 组内排序 [ROWS/RANGE 窗口范围定义] -- 指定计算范围 ) ``` --- #### 二、核心功能与示例 ##### 1. **分区计算(`PARTITION BY`)** 在指定列的分区内独立运算: ```sql -- 计算每个销售订单的总数量(保留明细行) SELECT SalesOrderID, ProductID, OrderQty, SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS TotalPerOrder FROM Sales.SalesOrderDetail; ``` ✅ **效果**: - 相同`SalesOrderID`的分区分别计算`SUM` - 结果保留所有原始行(不合并)[^4] ##### 2. **动态排序(`ORDER BY`)** 组内按指定列排序后运算: ```sql -- 按部门分组,给员工按薪资排名 SELECT orgid, username, salary, ROW_NUMBER() OVER(PARTITION BY orgid ORDER BY salary DESC) AS salary_rank FROM au_userinfo; ``` ✅ **输出**: | orgid | username | salary | salary_rank | |-------|----------|--------|-------------| | 101 | Alice | 9000 | 1 | | 101 | Bob | 8000 | 2 | | 102 | Carol | 8500 | 1 | [^3] ##### 3. **窗口范围控制(`ROWS/RANGE`)** 精确控制计算涉及的行范围: | **范围定义** | **含义** | |--------------------------------------|------------------------------| | `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW` | 从分区首行到当前行(累计计算) | | `ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING` | 当前行前1行到后1行(滑动窗口) | | `RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING` | 整个分区(默认)[^1] | **示例**:计算累计销售额 ```sql SELECT order_date, daily_sales, SUM(daily_sales) OVER(ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_sales FROM sales_data; ``` --- #### 三、常用窗口函数类型 | **函数类别** | **典型函数** | **用途** | |--------------------|----------------------------------|-------------------------------| | 聚合函数 | `SUM()`, `AVG()`, `COUNT()` | 分区内聚合计算(保留明细行) | | 排名函数 | `ROW_NUMBER()`, `RANK()`, `DENSE_RANK()` | 组内排序编号 | | 分析函数 | `LEAD()`, `LAG()`, `FIRST_VALUE()` | 跨行数据访问(如前/后行值) | --- #### 四、`OVER()` vs `GROUP BY` | **特性** | `OVER()` | `GROUP BY` | |---------------------------|---------------------------------|--------------------------| | 输出行数 | 保留所有原始行 | 每组仅一行(聚合结果) | | 计算粒度 | 行级计算 | 组级计算 | | 能否混合原始列与聚合值 | ✅ 是(如`SUM() OVER()`) | ❌ 需搭配`GROUP BY`所有非聚合列 | > ✅ **适用场景**: > - 需保留明细行的聚合计算(如计算占比、累计值) > - 组内排名(如销售TOP N) > - 时间序列分析(如移动平均) --- ### 总结 `OVER()`的核心价值在于: 1. **灵活分区**:通过`PARTITION BY`定义计算范围而不折叠数据 2. **动态排序**:结合`ORDER BY`实现组内有序计算(如排名) 3. **精细控制**:`ROWS/RANGE`精确指定计算窗口 4. **高效分析**:单次扫描完成复杂计算(如累计值、滑动平均) > 支持数据库:SQL Server, Oracle, PostgreSQL, MySQL 8.0+, BigQuery等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值