SQL Server 窗口函数全指南(函数用法与场景)

SQL Server 中的窗口函数(Window Functions)是一种强大的查询工具,它允许我们在查询结果集中对数据进行分区、排序和计算,而不会改变结果集的行数。窗口函数通过 OVER 子句定义一个"窗口",在该窗口内对数据进行操作。这使得我们能够轻松实现排名、聚合、移动平均等复杂计算,而无需使用子查询或自连接。

窗口函数的基本语法是:

sql 复制代码
窗口函数 OVER (
    [PARTITION BY 分区列]
    [ORDER BY 排序列 [ASC|DESC]]
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)
  • PARTITION BY:将结果集分成多个分区,每个分区独立计算。
  • ORDER BY:定义窗口内的排序顺序。
  • ROWS/RANGE:可选,定义窗口帧(frame),指定计算的行范围。

窗口函数主要分为三类:排名函数、聚合函数和分析函数。下面我们逐一介绍每个函数的具体用法和使用场景。为了说明,我们假设有一个名为 Sales 的表,结构如下:

OrderID Product Quantity Price OrderDate
1 A 10 100 2023-01-01
2 B 20 200 2023-01-02
3 A 15 150 2023-01-03
... ... ... ... ...

窗口函数完整列表

排名函数(Ranking Functions)

  1. ROW_NUMBER() - 为每行分配唯一的连续整数
  2. RANK() - 分配排名,相同值相同排名,有间隙
  3. DENSE_RANK() - 分配排名,相同值相同排名,无间隙
  4. NTILE(n) - 将数据分成n个组

聚合函数(Aggregate Functions)

  1. SUM() - 计算窗口内总和
  2. AVG() - 计算窗口内平均值
  3. MIN() - 计算窗口内最小值
  4. MAX() - 计算窗口内最大值
  5. COUNT() - 计算窗口内行数
  6. STDEV() - 计算窗口内标准差
  7. STDEVP() - 计算窗口内总体标准差
  8. VAR() - 计算窗口内方差
  9. VARP() - 计算窗口内总体方差

分析函数(Analytic Functions)

  1. LEAD() - 访问后续行的值
  2. LAG() - 访问前面行的值
  3. FIRST_VALUE() - 获取窗口内第一个值
  4. LAST_VALUE() - 获取窗口内最后一个值
  5. NTH_VALUE() - 获取窗口内第n个值

分布函数(Distribution Functions)

  1. PERCENT_RANK() - 计算相对排名(0-1)
  2. CUME_DIST() - 计算累计分布(0-1)
  3. PERCENTILE_CONT() - 连续百分位数
  4. PERCENTILE_DISC() - 离散百分位数

每个函数详细介绍

1. ROW_NUMBER()

函数说明:为结果集中的每一行分配一个唯一的连续整数,从1开始递增。相同值的行会获得不同的行号。

语法

sql 复制代码
ROW_NUMBER() OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

参数说明

  • PARTITION BY:可选,将结果集分成多个分区,每个分区独立编号
  • ORDER BY:必需,定义排序顺序,决定行号的分配

示例

sql 复制代码
-- 按产品分组,按数量降序编号
SELECT 
    Product,
    Quantity,
    ROW_NUMBER() OVER (PARTITION BY Product ORDER BY Quantity DESC) AS RowNum
FROM Sales;

-- 全局按价格降序编号
SELECT 
    Product,
    Price,
    ROW_NUMBER() OVER (ORDER BY Price DESC) AS GlobalRank
FROM Sales;

使用场景

  • 分页查询(LIMIT OFFSET)
  • 删除重复记录
  • 生成唯一标识符
  • 数据抽样

2. RANK()

函数说明:为行分配排名,相同值的行获得相同排名,下一个排名会跳过(产生间隙)。

语法

sql 复制代码
RANK() OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
-- 按价格排名,相同价格相同排名
SELECT 
    Product,
    Price,
    RANK() OVER (ORDER BY Price DESC) AS PriceRank
FROM Sales;

结果示例

复制代码
Product | Price | PriceRank
--------|-------|----------
A       | 200   | 1
B       | 200   | 1
C       | 150   | 3  (跳过了2)
D       | 100   | 4

使用场景

  • 竞赛排名
  • 成绩排名
  • 销售排行榜

3. DENSE_RANK()

函数说明:类似于RANK,但排名连续,没有间隙。相同值的行获得相同排名,下一个排名连续。

语法

sql 复制代码
DENSE_RANK() OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
SELECT 
    Product,
    Price,
    DENSE_RANK() OVER (ORDER BY Price DESC) AS DenseRank
FROM Sales;

结果示例

复制代码
Product | Price | DenseRank
--------|-------|----------
A       | 200   | 1
B       | 200   | 1
C       | 150   | 2  (连续,没有跳跃)
D       | 100   | 3

使用场景

  • 奖牌排名(金银铜)
  • 等级评定
  • 需要连续排名的场景

4. NTILE(n)

函数说明:将分区内的行分成n个组,每组分配一个从1到n的数字。如果行数不能被n整除,前面的组会多一行。

语法

sql 复制代码
NTILE(组数) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
-- 将数据分成4个四分位数
SELECT 
    Product,
    Price,
    NTILE(4) OVER (ORDER BY Price DESC) AS Quartile
FROM Sales;

使用场景

  • 分位数分析
  • 数据分桶
  • 等级划分(如A、B、C、D级)

5. SUM()

函数说明:计算窗口内列的总和,支持累计计算。

语法

sql 复制代码
SUM(列) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    [ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...]
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

窗口帧选项

  • ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - 累计到当前行
  • ROWS BETWEEN 3 PRECEDING AND CURRENT ROW - 当前行及前3行
  • ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - 当前行到末尾

示例

sql 复制代码
-- 累计销售总额
SELECT 
    OrderDate,
    Price,
    SUM(Price) OVER (ORDER BY OrderDate) AS RunningTotal
FROM Sales;

-- 按产品分组的累计销售
SELECT 
    Product,
    OrderDate,
    Price,
    SUM(Price) OVER (PARTITION BY Product ORDER BY OrderDate) AS ProductRunningTotal
FROM Sales;

-- 移动3天平均
SELECT 
    OrderDate,
    Price,
    AVG(Price) OVER (ORDER BY OrderDate ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MovingAvg3
FROM Sales;

使用场景

  • 财务累计报表
  • 销售趋势分析
  • 移动平均计算

6. AVG()

函数说明:计算窗口内列的平均值。

语法

sql 复制代码
AVG(列) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    [ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...]
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

示例

sql 复制代码
-- 每个产品的平均价格
SELECT 
    Product,
    Price,
    AVG(Price) OVER (PARTITION BY Product) AS AvgPrice
FROM Sales;

-- 移动平均
SELECT 
    OrderDate,
    Price,
    AVG(Price) OVER (ORDER BY OrderDate ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS MovingAvg5
FROM Sales;

使用场景

  • 趋势分析
  • 股票技术分析
  • 性能基准

7. MIN() / MAX()

函数说明:计算窗口内列的最小值/最大值。

语法

sql 复制代码
MIN(列) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    [ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...]
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

MAX(列) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    [ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...]
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

示例

sql 复制代码
-- 每个产品的历史最高价和最低价
SELECT 
    Product,
    Price,
    MIN(Price) OVER (PARTITION BY Product) AS MinPrice,
    MAX(Price) OVER (PARTITION BY Product) AS MaxPrice
FROM Sales;

使用场景

  • 价格监控
  • 极值分析
  • 范围计算

8. COUNT()

函数说明:计算窗口内的行数。

语法

sql 复制代码
COUNT(*) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    [ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...]
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

示例

sql 复制代码
-- 每个产品的订单数量
SELECT 
    Product,
    COUNT(*) OVER (PARTITION BY Product) AS ProductOrderCount
FROM Sales;

使用场景

  • 分组统计
  • 数据验证
  • 频率分析

9. LEAD()

函数说明:访问当前行后n行的值。如果超出范围,返回默认值。

语法

sql 复制代码
LEAD(列, n, 默认值) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

参数说明

  • :要访问的列
  • n:向后偏移的行数(默认为1)
  • 默认值:当超出范围时返回的值(默认为NULL)

示例

sql 复制代码
-- 计算价格变化
SELECT 
    OrderDate,
    Price,
    LEAD(Price, 1, 0) OVER (ORDER BY OrderDate) AS NextPrice,
    Price - LEAD(Price, 1, 0) OVER (ORDER BY OrderDate) AS PriceChange
FROM Sales;

使用场景

  • 时间序列分析
  • 增长率计算
  • 趋势预测

10. LAG()

函数说明:访问当前行前n行的值。如果超出范围,返回默认值。

语法

sql 复制代码
LAG(列, n, 默认值) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
-- 计算环比增长率
SELECT 
    OrderDate,
    Price,
    LAG(Price, 1, Price) OVER (ORDER BY OrderDate) AS PrevPrice,
    (Price - LAG(Price, 1, Price) OVER (ORDER BY OrderDate)) / 
    LAG(Price, 1, Price) OVER (ORDER BY OrderDate) * 100 AS GrowthRate
FROM Sales;

使用场景

  • 环比分析
  • 历史比较
  • 变化率计算

11. FIRST_VALUE()

函数说明:返回窗口帧中第一个值。

语法

sql 复制代码
FIRST_VALUE(列) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

示例

sql 复制代码
-- 每个产品的首次销售价格
SELECT 
    Product,
    OrderDate,
    Price,
    FIRST_VALUE(Price) OVER (PARTITION BY Product ORDER BY OrderDate) AS FirstPrice
FROM Sales;

使用场景

  • 基准值比较
  • 初始状态记录
  • 历史对比

12. LAST_VALUE()

函数说明:返回窗口帧中最后一个值。注意:默认窗口帧到当前行,通常需要调整。

语法

sql 复制代码
LAST_VALUE(列) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

示例

sql 复制代码
-- 每个产品的最新价格(需要调整窗口帧)
SELECT 
    Product,
    OrderDate,
    Price,
    LAST_VALUE(Price) OVER (
        PARTITION BY Product 
        ORDER BY OrderDate 
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS LastPrice
FROM Sales;

使用场景

  • 最新状态获取
  • 最终值比较
  • 状态跟踪

13. PERCENT_RANK()

函数说明:计算相对排名,返回0到1之间的值。第一名的值为0,最后一名的值为1。

语法

sql 复制代码
PERCENT_RANK() OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
-- 价格百分位排名
SELECT 
    Product,
    Price,
    PERCENT_RANK() OVER (ORDER BY Price DESC) AS PricePercentRank
FROM Sales;

使用场景

  • 统计分布分析
  • 百分位计算
  • 数据标准化

14. CUME_DIST()

函数说明:计算累计分布,返回0到1之间的值。表示小于等于当前值的行数占总行数的比例。

语法

sql 复制代码
CUME_DIST() OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
-- 价格累计分布
SELECT 
    Product,
    Price,
    CUME_DIST() OVER (ORDER BY Price) AS PriceCumeDist
FROM Sales;

使用场景

  • 分布分析
  • 分位数计算
  • 数据分布可视化

15. PERCENTILE_CONT()

函数说明:计算连续百分位数,返回插值后的值。

语法

sql 复制代码
PERCENTILE_CONT(百分位数) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
-- 计算中位数(50%分位数)
SELECT 
    Product,
    PERCENTILE_CONT(0.5) OVER (PARTITION BY Product ORDER BY Price) AS MedianPrice
FROM Sales;

使用场景

  • 统计分位数
  • 数据分布分析
  • 异常值检测

16. PERCENTILE_DISC()

函数说明:计算离散百分位数,返回实际存在的值。

语法

sql 复制代码
PERCENTILE_DISC(百分位数) OVER (
    [PARTITION BY 分区列1, 分区列2, ...]
    ORDER BY 排序列1 [ASC|DESC], 排序列2 [ASC|DESC], ...
)

示例

sql 复制代码
-- 计算离散中位数
SELECT 
    Product,
    PERCENTILE_DISC(0.5) OVER (PARTITION BY Product ORDER BY Price) AS DiscreteMedian
FROM Sales;

使用场景

  • 离散分位数
  • 实际值分析
  • 数据验证

排名函数

排名函数用于为行分配排名或分组。

1. ROW_NUMBER()

用法:为每个分区内的行分配一个唯一的连续整数,从1开始,按照 ORDER BY 排序。

语法

sql 复制代码
ROW_NUMBER() OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

sql 复制代码
SELECT 
    Product, 
    Quantity, 
    ROW_NUMBER() OVER (PARTITION BY Product ORDER BY Quantity DESC) AS RowNum
FROM Sales;

结果(假设数据):

  • A, 15, 1
  • A, 10, 2
  • B, 20, 1

使用场景:分页查询、生成唯一行号、删除重复记录(结合 CTE)。

2. RANK()

用法:为行分配排名,如果值相同则排名相同,下一个排名会跳过(有间隙)。

语法

sql 复制代码
RANK() OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

sql 复制代码
SELECT 
    Product, 
    Quantity, 
    RANK() OVER (ORDER BY Quantity DESC) AS Rank
FROM Sales;

结果

  • B, 20, 1
  • A, 15, 2
  • A, 10, 3 (没有跳跃,因为没有并列)

如果有两个15,则:15排2,下一个跳到4。

使用场景:排名竞赛、识别前N名,但允许并列。

3. DENSE_RANK()

用法:类似于 RANK,但排名连续,没有间隙。

语法

sql 复制代码
DENSE_RANK() OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子:同上,如果有两个15,则:15排2,下一个排3。

使用场景:需要连续排名的场景,如奖牌排名(金银铜连续)。

4. NTILE(n)

用法:将分区内的行分成 n 个组,每组分配一个从1到n的数字。

语法

sql 复制代码
NTILE(组数) OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

sql 复制代码
SELECT 
    Product, 
    Quantity, 
    NTILE(2) OVER (ORDER BY Quantity DESC) AS Tile
FROM Sales;

结果:分成两组,前半组1,后半组2。

使用场景:分桶分析、将数据分成等份(如分位数)。

聚合函数

聚合函数如 SUM、AVG、MIN、MAX、COUNT 可以与 OVER 结合,在窗口内计算。

1. SUM()

用法:计算窗口内列的总和。

语法

sql 复制代码
SUM(列) OVER (PARTITION BY 分区列 ORDER BY 排序列 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)

例子(累计销售):

sql 复制代码
SELECT 
    OrderDate, 
    Price, 
    SUM(Price) OVER (ORDER BY OrderDate) AS RunningTotal
FROM Sales;

结果:每行显示到当前日期的累计总价。

使用场景:运行总计、累计求和、财务报告。

2. AVG()

用法:计算平均值。

语法:类似 SUM。

例子:计算移动平均。

使用场景:趋势分析、股票移动平均。

3. MIN() / MAX()

用法:窗口内最小/最大值。

例子:查找每个产品的历史最低价。

使用场景:价格监控、极值分析。

4. COUNT()

用法:计数。

例子:计算每个分区内的行数。

使用场景:分组计数而不使用 GROUP BY。

分析函数

分析函数用于访问窗口中其他行的数据。

1. LEAD(列, n, 默认值)

用法:返回当前行后 n 行的列值。

语法

sql 复制代码
LEAD(列, n, 默认值) OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

sql 复制代码
SELECT 
    OrderDate, 
    Price, 
    LEAD(Price, 1, 0) OVER (ORDER BY OrderDate) AS NextPrice
FROM Sales;

结果:显示下一订单的价格。

使用场景:比较前后行、计算增长率、时间序列分析。

2. LAG(列, n, 默认值)

用法:返回当前行前 n 行的列值。

语法:类似 LEAD。

例子:计算价格变化。

使用场景:与 LEAD 类似,用于历史比较。

3. FIRST_VALUE(列)

用法:返回窗口帧中第一个值。

语法

sql 复制代码
FIRST_VALUE(列) OVER (PARTITION BY 分区列 ORDER BY 排序列 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)

例子:每个产品的首次销售价格。

使用场景:基准值比较。

4. LAST_VALUE(列)

用法:返回窗口帧中最后一个值。注意:默认帧到当前行,需要调整。

例子:每个产品的最新价格。

使用场景:最新状态获取。

其他函数

1. PERCENT_RANK()

用法:计算相对排名(0到1)。

语法:OVER (ORDER BY 排序列)

例子:百分位排名。

使用场景:统计分布。

2. CUME_DIST()

用法:累计分布(0到1)。

使用场景:分布分析。

结论

SQL Server 窗口函数极大地简化了复杂查询,提高了效率。通过掌握这些函数,你可以处理各种数据分析任务。建议在实际项目中练习,以加深理解。注意:窗口函数不支持在 WHERE 或 GROUP BY 中使用,通常在 SELECT 或 ORDER BY 中。

相关推荐
hweiyu006 分钟前
Scala实用编程(附电子书资料)
开发语言·后端·scala
mftang10 分钟前
C 标准库 <time.h> 函数详解
c语言·开发语言
lly20240611 分钟前
SVG 在线编辑器
开发语言
脑袋大大的38 分钟前
uni-app x开发避坑指南:拯救被卡顿的UI线程!
开发语言·前端·javascript·vue.js·ui·uni-app·uts
测试199841 分钟前
Newman+Jenkins实施接口自动化测试
自动化测试·软件测试·python·测试工具·职场和发展·jenkins·测试用例
踏过山河,踏过海1 小时前
Django自带的加密算法
数据库·django·sqlite
feuiw1 小时前
django-3模型操作
python·django
计算机毕设定制辅导-无忧学长1 小时前
InfluxDB 与 Python 框架结合:Django 应用案例(一)
python·django·sqlite
Estrella_1 小时前
解决pd.cut后groupby出现的警告:深入理解observed参数
python