MsSql

关注公众号 jb51net

关闭
首页 > 数据库 > MsSql > SQL Server窗口函数

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

作者:nbsaas-boot

窗口函数是整个SQL语句最后被执行的部分,这意味着窗口函数是在SQL查询的结果集上进行的,因此不会受到Group By, Having,Where子句的影响,这篇文章主要介绍了SQL Server窗口函数的相关资料,需要的朋友可以参考下

前言

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

窗口函数的基本语法是:

窗口函数 OVER (
    [PARTITION BY 分区列]
    [ORDER BY 排序列 [ASC|DESC]]
    [ROWS | RANGE BETWEEN 边界1 AND 边界2]
)

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

OrderIDProductQuantityPriceOrderDate
1A101002023-01-01
2B202002023-01-02
3A151502023-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开始递增。相同值的行会获得不同的行号。

语法

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

参数说明

示例

-- 按产品分组,按数量降序编号
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;

使用场景

2. RANK()

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

语法

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

示例

-- 按价格排名,相同价格相同排名
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,但排名连续,没有间隙。相同值的行获得相同排名,下一个排名连续。

语法

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

示例

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整除,前面的组会多一行。

语法

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

示例

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

使用场景

5. SUM()

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

语法

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

窗口帧选项

示例

-- 累计销售总额
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()

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

语法

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

示例

-- 每个产品的平均价格
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()

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

语法

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]
)

示例

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

使用场景

8. COUNT()

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

语法

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

示例

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

使用场景

9. LEAD()

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

语法

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

参数说明

示例

-- 计算价格变化
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行的值。如果超出范围,返回默认值。

语法

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

示例

-- 计算环比增长率
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()

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

语法

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

示例

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

使用场景

12. LAST_VALUE()

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

语法

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

示例

-- 每个产品的最新价格(需要调整窗口帧)
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。

语法

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

示例

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

使用场景

14. CUME_DIST()

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

语法

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

示例

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

使用场景

15. PERCENTILE_CONT()

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

语法

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

示例

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

使用场景

16. PERCENTILE_DISC()

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

语法

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

示例

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

使用场景

排名函数

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

1. ROW_NUMBER()

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

语法

ROW_NUMBER() OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

SELECT 
    Product, 
    Quantity, 
    ROW_NUMBER() OVER (PARTITION BY Product ORDER BY Quantity DESC) AS RowNum
FROM Sales;

结果(假设数据):

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

2. RANK()

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

语法

RANK() OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

SELECT 
    Product, 
    Quantity, 
    RANK() OVER (ORDER BY Quantity DESC) AS Rank
FROM Sales;

结果

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

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

3. DENSE_RANK()

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

语法

DENSE_RANK() OVER (PARTITION BY 分区列 ORDER BY 排序列)

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

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

4. NTILE(n)

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

语法

NTILE(组数) OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

SELECT 
    Product, 
    Quantity, 
    NTILE(2) OVER (ORDER BY Quantity DESC) AS Tile
FROM Sales;

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

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

聚合函数

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

1. SUM()

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

语法

SUM(列) OVER (PARTITION BY 分区列 ORDER BY 排序列 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)

例子(累计销售):

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 行的列值。

语法

LEAD(列, n, 默认值) OVER (PARTITION BY 分区列 ORDER BY 排序列)

例子

SELECT 
    OrderDate, 
    Price, 
    LEAD(Price, 1, 0) OVER (ORDER BY OrderDate) AS NextPrice
FROM Sales;

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

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

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

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

语法:类似 LEAD。

例子:计算价格变化。

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

3. FIRST_VALUE(列)

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

语法

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 中。

到此这篇关于SQL Server窗口函数的文章就介绍到这了,更多相关SQL Server窗口函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文