优雅统计,轻松计算——窥探开窗函数的神秘面纱
在数据分析领域中,我们经常需要对数据进行多种统计计算,如求和、均值、中位数等等。而在实际应用中,一些统计计算需要以一定的窗口进行计算,比如最近N天销售额、最近K个月的平均利润率等等。此时,开窗函数就成了一个非常有用的工具。本文将围绕开窗函数,介绍其概念、使用方法以及常见的应用场景。
一、概念解析
开窗函数(Window Function)是一种在关系型数据库中进行统计计算的函数。它能够对查询结果进行分析、排序、分组,从而实现对数据的按窗口进行计算。开窗函数通常会跟随在SELECT语句的列之后,并且在OVER子句中定义窗口的范围。
具体来说,使用开窗函数时需要先定义一个窗口(Window),窗口的定义有三个要素:排序规则(Order)、分组规则(Partition)和窗口范围(Frame)。
排序规则:用于指定窗口中的行按照哪个字段进行排序,语法格式为ORDER BY(类似于普通的ORDER BY语句)。
分组规则:用于将窗口中的数据按照某个字段进行分组,语法格式为PARTITION BY。
窗口范围:用于指定计算当前行的范围,通常是基于当前行相对于窗口范围内的其他行的位置或者值来确定的。常用的窗口范围包括:
- ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:窗口从开头开始,一直持续到当前行;
- ROWS BETWEEN N PRECEDING AND CURRENT ROW:窗口从当前行往前N行,一直持续到当前行;
- ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING:窗口从开头开始,一直持续到末尾行;
- RANGE BETWEEN N PRECEDING AND CURRENT ROW:窗口从当前行向前延伸N个单位,一直持续到当前行,其中N的单位可以是时间(比如一天、一周等等)或者其他可以比较的数据类型(例如身高、年龄等等)。
二、使用方法
下面通过一个例子介绍开窗函数的具体使用方法。我们考虑一张表orders,其中包含了所有订单的信息,订单包括订单号、订单时间以及订单金额等。
我们现在想要统计每个月的销售总金额,并且想要得到2个值:每个月的销售总金额以及截止到目前为止累计的总销售金额。可以使用以下方法进行查询:
SELECT
DATE_TRUNC('month', order_date) as month,
SUM(amount) as monthly_sales,
SUM(SUM(amount)) OVER (ORDER BY DATE_TRUNC('month', order_date) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as cumulative_sales
FROM
orders
GROUP BY
month
ORDER BY
month
上述语句中,首先通过DATE_TRUNC函数将订单时间戳按照月份进行截断,并将其作为窗口分组规则;其次,通过SUM函数对每个分组内的销售金额进行求和;最后,在求和结果上再次调用SUM函数,使用OVER子句定义窗口范围,将累计销售金额的结果计算出来。上述语句的运行结果如下表所示:
| month | monthly_sales | cumulative_sales |
|----------------------|----------------|------------------|
| 2021-01-01 00:00:00 | 300 | 300 |
| 2021-02-01 00:00:00 | 500 | 800 |
| 2021-03-01 00:00:00 | 200 | 1000 |
从运行结果来看,我们得到了每个月的销售总金额以及截止到目前为止累计的总销售金额两个值。这样,使用开窗函数可以很方便地实现对复杂统计计算的分析。
三、应用场景
开窗函数在实际应用中有很多使用场景。以下是几个常见的应用场景:
1. 统计周期内的销售额、利润率等等
在实际应用中,我们经常需要对某个时间周期内的销售额、利润率等等进行统计分析。这个时候,开窗函数可以轻松地实现对周期内的数据进行聚合。
例如,我们可以使用以下语句来查询最近7天的销售额以及每天的销售额均值:
SELECT
DATE_TRUNC('day', order_date) as day,
SUM(amount) as total_sales,
AVG(amount) OVER (ORDER BY DAY ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as avg_sales
FROM
orders
WHERE
order_date >= DATE_TRUNC('day', CURRENT_TIMESTAMP - INTERVAL '7' DAY)
GROUP BY
day
ORDER BY
day
在以上语句中,我们使用了ORDER BY DAY来将数据按照天进行排序,再使用ROWS BETWEEN 6 PRECEDING AND CURRENT ROW定义一个7天的滑动窗口来计算每天的销售额均值。这样,我们就可以很方便地对最近7天的销售额进行分析。
2. 数据排名、排名变化等分析
使用开窗函数也可以实现对数据排名、排名变化等分析。例如,我们可以通过以下语句查询出每位学生的语文、数学、英语成绩以及总成绩以及排名情况:
SELECT
name,
chinese,
math,
english,
chinese + math + english as total_score,
RANK() OVER (
ORDER BY chinese + math + english DESC
) as rank
FROM
scores
在以上语句中,我们使用RANK函数依据总成绩的值来为每个学生进行排名,并在查询结果中输出学生的排名情况。这样,我们就可以方便地进行比较分析,了解每个学生的成绩表现。
3. 分析序列数据、时间序列数据等
开窗函数还可以用于分析序列数据、时间序列数据等。例如,在分析网站访问记录时,我们可以使用开窗函数来分析用户的访问顺序、停留时间等等信息。
具体来说,我们可以使用以下语句查询每位用户在每个页面上的浏览停留时间:
SELECT
user_id,
page,
LEAD(timestamp) OVER (PARTITION BY user_id ORDER BY timestamp) - timestamp as stay_time
FROM
pageviews
在以上语句中,我们使用LEAD函数将用户在当前页面之后访问下一个页面的时间戳减去当前时间戳,从而得到用户在当前页面上的停留时间。这样,我们就可以通过对这些数据进行进一步的处理,比如按照页面分类统计每个页面的平均停留时间等等。
四、总结
本文围绕开窗函数展开,介绍了开窗函数的概念、使用方法以及常见的应用场景。开窗函数可以方便地实现对数据的按窗口进行计算,丰富了数据分析人员的工具箱。在实际应用中,数据分析人员可以结合具体应用场景,灵活应用开窗函数,提高数据分析的效率和准确性。