乍一看,窗口函数和聚合函数好像都是分析和处理数据的工具。毕竟它们都能做 sum、avg、排名啥的。但其实它们本质上还是有点不一样的,咱们来仔细聊聊。
聚合函数(GROUP BY)
聚合函数是这么干活的:
- 它们会按照你指定的列把行分组。
- 分组后,每个组只剩下一行结果。
- 举个例子:你想知道每个地区的总收入。
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
特点:GROUP BY 会把数据“压缩”掉。你用分组后,属于同一组的所有行都没了——只剩下聚合的结果。
窗口函数(PARTITION BY)
窗口函数则完全不一样:
- 它会保留原始数据结构(不会压缩,也不会让行消失!)。
- 可以在“窗口”——逻辑上分开的行组里做计算。
举个例子:你想知道每个城市在它所在地区的销售占比,但又想保留所有数据。
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
特点:用窗口函数不会删掉任何行,只是给每行加上新的计算值。
例子:SUM() 配 GROUP BY vs SUM() 配 PARTITION BY
为了更直观地理解区别,咱们看看 SUM() 在这两种情况下怎么表现。假设有个表 sales_data,长这样:
| region | city | sales |
|---|---|---|
| North | CityA | 100 |
| North | CityB | 150 |
| South | CityC | 200 |
| South | CityD | 250 |
用 GROUP BY 求和
我们想知道每个地区的总销售额:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
结果会是这样:
| region | total_sales |
|---|---|
| North | 250 |
| South | 450 |
发生了啥:行被 region 分组,每组被“压缩”成一行,显示销售总额。
用 PARTITION BY 求和
现在用窗口函数来做同样的事:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
结果:
| region | city | sales | total_sales_by_region |
|---|---|---|---|
| North | CityA | 100 | 250 |
| North | CityB | 150 | 250 |
| South | CityC | 200 | 450 |
| South | CityD | 250 | 450 |
发生了啥:PARTITION BY 没有“压缩”行,而是在每个窗口(每个地区)里算了个总和,结果加到每行上。
啥时候用 GROUP BY,啥时候用 PARTITION BY?
GROUP BY:适合做最终报表
GROUP BY 很适合你想缩减数据量、只要每组的最终结果的时候。比如:
- 每个月的总销售额。
- 按商品类别统计订单数。
例子:
SELECT category, COUNT(*) AS total_orders
FROM orders
GROUP BY category;
PARTITION BY:超适合分析和细节展示
PARTITION BY 适合你想保留所有数据行,还想给每行多算点啥的时候。比如:
- 算每个商品在类别里的销售占比。
- 每组内给行编号。
算销售占比的例子:
SELECT
category,
product,
sales,
ROUND(
(sales * 100.0) / SUM(sales) OVER (PARTITION BY category),
2
) AS sales_percentage
FROM sales_data;
例子:用多个窗口函数
窗口函数的一个牛逼之处就是可以一次用好几个计算。比如:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS sales_rank
FROM sales_data;
结果:
| region | city | sales | total_sales | sales_rank |
|---|---|---|---|---|
| North | CityB | 150 | 250 | 1 |
| North | CityA | 100 | 250 | 2 |
| South | CityD | 250 | 450 | 1 |
| South | CityC | 200 | 450 | 2 |
窗口函数比 GROUP BY 牛在哪?
保留原始数据: GROUP BY 会“压缩”行,窗口函数能让表结构保持原样。
一个查询搞定多个计算: 你可以用不同参数的 PARTITION BY 和 ORDER BY,同时做多个窗口函数计算,还能保留所有数据。
分析更灵活: 窗口函数能让你自定义各种计算:累计和、排名、占比啥的,想怎么玩怎么玩。
灵活性的例子
来试试把几个函数合起来:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
AVG(sales) OVER (PARTITION BY region) AS avg_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS rank
FROM sales_data;
结果:
| region | city | sales | total_sales | avg_sales | rank |
|---|---|---|---|---|---|
| North | CityB | 150 | 250 | 125.0 | 1 |
| North | CityA | 100 | 250 | 125.0 | 2 |
| South | CityD | 250 | 450 | 225.0 | 1 |
| South | CityC | 200 | 450 | 225.0 | 2 |
限制和常见错误
有个常见坑,就是你想“压缩”数据时却用 PARTITION BY。比如,正确写法:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
有些人会写成这样:
SELECT
region,
SUM(sales) OVER (PARTITION BY region) AS total_sales
FROM sales_data;
但这样会返回所有行,数据量一点没变(有时候这不是你想要的)。
现在你肯定知道啥时候用 GROUP BY,啥时候用窗口函数了。这就像选锤子还是螺丝刀:都能搞钉子……但用法不一样。
GO TO FULL VERSION