OVER(PARTITION BY... ORDER BY...)的使用说明和意义

本文详细介绍了SQL中OVER函数的使用方法,包括PARTITION BY和ORDER BY的功能,并通过具体案例展示了如何利用OVER函数配合ROW_NUMBER(), RANK(), DENSE_RANK(), SUM(), FIRST_VALUE(), LAST_VALUE()等函数实现数据排序、分组、求和等功能。

OVER(PARTITION BY... ORDER BY...)的使用说明和意义

这个函数长这个样子:OVER(PARTITION BY... ORDER BY...)

顾名思义,PARTITION 中文是分割的意思,ORDER 是排序的意思,所以翻译一下就是先把一组数据按照制定的字段进行分割成各种组,然后组内按照某个字段排序。

以实际案例来说明这个函数的使用,

首先,我们先看一组普通排序的数据:


然后对其进行新加一个伪劣ROWNUM,看看数据原本的顺序:


OVER(ORDER BY ...)的意思是以按照某个字段排序,所以和直接对表的order by效果是一样的:


这里就显示了OVER(ORDER BY ...)的第一个功能,就是对新查询得到的数据进行重新编号,即RNO的值,由于这里没有PARTITION BY...,所以可以比作整个列就是一个大块,然后对大块的内容进行排序,这个时候再加上PARTITION BY...,查询结果:


它会按照制定的字段,把相同值的排在一起,分为一个块,也就是分组,然后组内排序编号,这样你就可以取不同组内的任意第几个值,类似于TOP-N的分析。

在这里over之前使用的函数是ROW_NUMBER(),就是对数据的编号。


与OVER(PARTITION BY... ORDER BY...)匹配使用的函数

row_number() over()、rank() over()和dense_rank() over()

       在上面的例子里,使用ROW_NUMBER()可以对数据编号,但是有一个问题,例子中的MI_ID是不可以重复的,如果在可以重复的情况下,就有并列的情况,这样就无法取出并列的数据,只能取单一排序的数据。所以这里可以换成 rank() over()和dense_rank() over()来进行编号:(rank() over()和dense_rank() over()的区别如图)



sum() over(),first_value() over()和last_value() over()的使用

sum() over()分组求和

first_value() over()求分组第一条

last_value() over()求分组最后一条

其中用row_number() over()取编号第一条的也可以实现first_value() over()的效果

SELECT DISTINCT *
  FROM (SELECT T1.MI_ID,
               T3.I_IDENTITY_CARD,
               SUM(NVL(T2.IS_VISHEARTPROMEMBER, 0)) OVER(PARTITION BY T3.I_IDENTITY_CARD) PRO_NUM,
               SUM(NVL(T2.IS_VISHEARTEXPMEMBER, 0)) OVER(PARTITION BY T3.I_IDENTITY_CARD) EXP_NUM,
               SUM(NVL(T2.IS_VISHEARTPOLMEMBER, 0)) OVER(PARTITION BY T3.I_IDENTITY_CARD) POL_NUM,
							 T1.CREATE_TIME,
               FIRST_VALUE(T2.CREATE_DATE) OVER(PARTITION BY T3.I_IDENTITY_CARD ORDER BY T2.CREATE_DATE DESC) FIRST_CREATE_DATE
          FROM T_ZZ_PETITIONERS T1
          LEFT JOIN T_ZZ_VISIT_RECORD T2
            ON T1.CI_RS_ID = T2.CI_RS_ID
           AND T2.STATUS != '003'
          LEFT JOIN T_DC_CI_RS_TOP T3
            ON T1.CI_RS_ID = T3.CI_RS_ID
           AND T3.STATUS = '1'
         WHERE T1.IS_ADD_HEARTPROTECT = '1'
           AND T1.STATUS = '1') C
 ORDER BY C.CREATE_TIME DESC
FIRST_VALUE(T2.CREATE_DATE) OVER(PARTITION BY T3.I_IDENTITY_CARD ORDER BY T2.CREATE_DATE DESC) FIRST_CREATE_DATE
          FROM T_ZZ_PETITIONERS T1
          LEFT JOIN T_ZZ_VISIT_RECORD T2
            ON T1.CI_RS_ID = T2.CI_RS_ID
           AND T2.STATUS != '003'
          LEFT JOIN T_DC_CI_RS_TOP T3
            ON T1.CI_RS_ID = T3.CI_RS_ID
           AND T3.STATUS = '1'
         WHERE T1.IS_ADD_HEARTPROTECT = '1'
           AND T1.STATUS = '1') C
 ORDER BY C.CREATE_TIME DESC



SELECT *
  FROM (SELECT T1.MI_ID,
               T3.I_IDENTITY_CARD,
               SUM(NVL(T2.IS_VISHEARTPROMEMBER, 0)) OVER(PARTITION BY T3.I_IDENTITY_CARD) PRO_NUM,
               SUM(NVL(T2.IS_VISHEARTEXPMEMBER, 0)) OVER(PARTITION BY T3.I_IDENTITY_CARD) EXP_NUM,
               SUM(NVL(T2.IS_VISHEARTPOLMEMBER, 0)) OVER(PARTITION BY T3.I_IDENTITY_CARD) POL_NUM,
               T1.CREATE_TIME,
               ROW_NUMBER() OVER(PARTITION BY T3.I_IDENTITY_CARD ORDER BY T2.CREATE_DATE DESC) RNO
          FROM T_ZZ_PETITIONERS T1
          LEFT JOIN T_ZZ_VISIT_RECORD T2
            ON T1.CI_RS_ID = T2.CI_RS_ID
           AND T2.STATUS != '003'
          LEFT JOIN T_DC_CI_RS_TOP T3
            ON T1.CI_RS_ID = T3.CI_RS_ID
          LEFT JOIN T_DC_GRID T4
            ON T1.REGION_CODE = T4.INFO_ORG_CODE
         WHERE T1.IS_ADD_HEARTPROTECT = '1'
           AND T1.STATUS = '1'
           AND T3.STATUS = '1'
           AND T4.STATUS = '001') C
 WHERE C.RNO = 1
 ORDER BY C.CREATE_TIME DESC
ROW_NUMBER() OVER(PARTITION BY T3.I_IDENTITY_CARD ORDER BY T2.CREATE_DATE DESC) RNO
          FROM T_ZZ_PETITIONERS T1
          LEFT JOIN T_ZZ_VISIT_RECORD T2
            ON T1.CI_RS_ID = T2.CI_RS_ID
           AND T2.STATUS != '003'
          LEFT JOIN T_DC_CI_RS_TOP T3
            ON T1.CI_RS_ID = T3.CI_RS_ID
          LEFT JOIN T_DC_GRID T4
            ON T1.REGION_CODE = T4.INFO_ORG_CODE
         WHERE T1.IS_ADD_HEARTPROTECT = '1'
           AND T1.STATUS = '1'
           AND T3.STATUS = '1'
           AND T4.STATUS = '001') C
 WHERE C.RNO = 1
 ORDER BY C.CREATE_TIME DESC



ignore nulls:过滤掉空值


常用的分析函数如下所列:
row_number() over(partition by ... order by ...) 
rank() over(partition by ... order by ...) 
dense_rank() over(partition by ... order by ...) 
count() over(partition by ... order by ...) 
max() over(partition by ... order by ...) 
min() over(partition by ... order by ...) 
sum() over(partition by ... order by ...) 
avg() over(partition by ... order by ...) 
first_value() over(partition by ... order by ...) 
last_value() over(partition by ... order by ...) 
lag() over(partition by ... order by ...) 
lead() over(partition by ... order by ...)

--根据DEFECT_ID作为分组,按创建时间倒序,取第一条记录(最新执行记录)

select * from 
( select RANK() OVER(partition by W.DEFECT_ID ORDER BY W.CREAT_TIME DESC) ROWNUMBER,W.* from t_defect_work_log W<br>) 
where ROWNUMBER=1;
</article>
<think> 我们被要求解释窗口函数 SUM() OVER (PARTITION BY ...)使用方法。根据引用[1][2][3],我们可以知道: 1. 窗口函数的基本结构:function_name(<argument>) OVER (<partition_Clause> <order by_Clause> <windowing_Clause>) 2. SUM() OVER (PARTITION BY ...) 用于对某个字段进行分组求,同时保持原表的行数(即每行都会返回结果,而不是像GROUP BY那样聚合为一行) 3. 其中,PARTITION BY 用于分组,类似于GROUP BY的分组,但不会合并行;ORDER BY 用于指定组内的排序,如果指定了ORDER BY,则默认的窗口范围是从组内第一行到当前行(即累计求);ROWS子句可以指定窗口的范围。 我们将按照以下结构回答: 1. 基本语法 2. 核心组成部分(PARTITION BY, ORDER BY, 窗口范围) 3. 使用示例 4. 常见使用场景 5. 注意事项 注意:由于用户要求使用中文,并且所有数学公式使用LaTeX格式,我们在示例中会使用代码块展示SQL语句,并在适当的地方用中文解释。 </think> ### 窗口函数 `SUM() OVER (PARTITION BY ...)` 详解 #### 1. **基本语法结构** ```sql SUM(column) OVER ( PARTITION BY partition_expression ORDER BY sort_expression [ASC|DESC] [ROWS/RANGE BETWEEN frame_start AND frame_end] ) ``` - **`SUM(column)`**:对指定列进行求 - **`PARTITION BY`**:将数据分割为独立的分组(类似`GROUP BY`但保留明细行)[^1][^4] - **`ORDER BY`**:定义组内计算顺序(影响累计计算方向)[^2] - **`ROWS/RANGE`**:可选子句,定义计算窗口范围[^1][^3] #### 2. **核心功能场景** | 场景 | SQL 示例 | 效果说明 | |------|----------|----------| | **分组累计求** | `SUM(sales) OVER(PARTITION BY product)` | 按产品分组计算销售总额(每行显示该产品总) | | **滚动累计求** | `SUM(sales) OVER(PARTITION BY dept ORDER BY date)` | 按部门+日期顺序计算累计销售额[^2] | | **自定义范围求** | `SUM(sales) OVER(ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)` | 计算当前行及前后相邻行的销售 | #### 3. **完整使用示例** ```sql SELECT employee_id, department, sale_date, daily_sales, -- 部门销售总额(每行相同) SUM(daily_sales) OVER(PARTITION BY department) AS dept_total, -- 部门内按日期累计销售额 SUM(daily_sales) OVER( PARTITION BY department ORDER BY sale_date ) AS running_total, -- 当前员工在部门内销售占比 daily_sales / SUM(daily_sales) OVER(PARTITION BY department) * 100 AS sales_percent FROM sales_records; ``` #### 4. **关键参数说明** - **`PARTITION BY NULL`** 全表作为单一分区:`SUM(sales) OVER()` → 全表销售总 - **多字段分区** `PARTITION BY year, region` → 按年份地区双重分组 - **窗口帧控制** ```sql ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -- 从首行到当前行(默认)[^3] ROWS BETWEEN 2 PRECEDING AND CURRENT ROW -- 当前行+前2行 RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW -- 近7天范围 ``` #### 5. **与 GROUP BY 的对比** | 特性 | 窗口函数 | GROUP BY | |------|----------|----------| | **输出行数** | 保持原表行数 | 分组合并为单行 | | **明细可见性** | 可同时显示明细聚合结果 | 明细不可见 | | **多聚合层级** | 单SQL语句实现多层聚合 | 需多次JOIN或子查询 | > 💡 **适用场景**:需保留明细数据的同时展示分组聚合结果,如计算累计占比、移动平均值、排名等分析场景[^1][^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值