Partition VS Group

前言

在了解数据倾斜之前,我们先了解一下什么是partition

partition

partition是一种开窗函数。用法为:

1
2
SELECT column1 OVER(PARTITION BY column2 ORDER BY column3 DESC)
FROM table1;

当然,如果使用sqlite,你会发现很多情况下都会加上一个中括号:

1
2
SELECT [column1] OVER(PARTITION BY [column2] ORDER BY [column3] DESC)
FROM table1;

这都是小事。

group

group是一种聚合函数。用法为:

1
2
3
4
SELECT column1
FROM table1
GROUP BY column2
ORDER BY column3 DESC;

同样的,你也可以加上中括号。

对比

使用partitiongroup时,虽然说都有分组的功能,但是原理上稍微有些不同。

在使用group的时候,最终结果并不保存所有数据,而是只保存分组后的数据,每个组中需要SELECT的属性仅保留唯一的属性值。

比如我现在有一个表,有四个属性,分别是idnamemoneyaddress

id name money address
1 张三 18 北京分公司
2 李四 19 上海分公司
3 王五 20 北京分公司
4 马六 21 北京分公司
5 赵七 22 武汉分公司
6 钱八 23 杭州分公司
7 孙九 24 杭州分公司

如果我使用group

1
2
3
4
SELECT id, name, money, address
FROM table1
GROUP BY address
ORDER BY money DESC;

那么最终结果为:

id name money address
1 张三 18 北京分公司
2 李四 19 上海分公司
5 赵七 22 武汉分公司
6 钱八 23 杭州分公司

在这个基础上使用聚合函数的话,也只能保有这些信息。部分分组的名称被吞掉了。

如果说这是一个绩效表,需要查看谁(帕鲁)在本年度创收占所在公司占比情况,使用GROUP BY需要保证每个分组并不会被吞掉。这也意味着需要考虑重名、金额相等、分公司人数较多等情况。

P.S.:并不是所有的公司都是这么变态的。

而如果使用partition

1
2
SELECT id, name, money, address, ROUND(money / SUM(money) OVER(PARTITION BY address ORDER BY money), 2) AS ratio
FROM table1

他的结果是:

id name money address ratio
1 张三 18 北京分公司 0.31
2 李四 19 上海分公司 1.00
3 王五 20 北京分公司 0.34
4 马六 21 北京分公司 0.35
5 赵七 22 武汉分公司 1.00
6 钱八 23 杭州分公司 0.49
7 孙九 24 杭州分公司 0.51

清晰地将所有信息全部罗列出来了。

如果需要排序,或者在排序的基础上获取前几,那就可以使用嵌套查询:

1
2
3
4
5
SELECT t.name, t.address, rank() OVER(PARTITION BY t.[address] ORDER BY t.ratio) FROM (
SELECT [name], [money], [address], ROUND(
[money] * 100 / SUM([money]) OVER(PARTITION BY [address] ORDER BY [money]), 2
) AS ratio FROM item_list
) AS t;

最终结果就是:

name address ratio rank
马六 北京分公司 0.35 1
王五 北京分公司 0.34 2
张三 北京分公司 0.31 3
李四 上海分公司 1.00 1
赵七 武汉分公司 1.00 1
孙九 杭州分公司 0.51 1
钱八 杭州分公司 0.49 2

看来,使用partition可以更直观地获取数据,并且最大程度保留每个分组中每个属性的信息,从而实现更丰富的需求。