Eva*_*oll 9 postgresql window-functions gaps-and-islands rank
如何用和解决gaps-and-islands的孤岛部分。我现在已经看过几次了,我想知道是否有人可以解释一下,dense_rank()row_number()
让我们使用这样的东西作为示例数据(示例使用 PostgreSQL),
CREATE TABLE foo
AS
SELECT x AS id, trunc(random()*3+1) AS x
FROM generate_series(1,50)
AS t(x);
Run Code Online (Sandbox Code Playgroud)
这应该产生这样的东西。
id | x
----+---
1 | 3
2 | 1
3 | 3
4 | 3
5 | 3
6 | 2
7 | 3
8 | 2
9 | 1
10 | 3
...
Run Code Online (Sandbox Code Playgroud)
我们想要的是这样的...... (z我们可以使用的价值在哪里GROUP BY)
id | x | grp
----+------
1 | 3 | z
2 | 1 | z
3 | 3 | z
4 | 3 | z
5 | 3 | z
6 | 2 | z
7 | 3 | z
8 | 2 | z
9 | 1 | z
10 | 3 | z
...
Run Code Online (Sandbox Code Playgroud)
这样我们可以做任何一个
GROUP BY x,grp哪里(x,grp)有独特的岛屿GROUP BY grp哪里grp是一个独特的岛屿。在上面的例子中,(x,grp) 看起来像这样.. (z我们可以使用的值在哪里GROUP BY)
id | x | grp
----+------
1 | 3 | 1
2 | 1 | 1
3 | 3 | 2
4 | 3 | 2
5 | 3 | 2
6 | 2 | z -- Because we're grouping by an (x,grp)
7 | 3 | z -- Does not have to be `3` (or something unique)
8 | 2 | z -- z=1, is fine to use again if we
9 | 1 | z -- GROUP BY (x,grp)
10 | 3 | z
Run Code Online (Sandbox Code Playgroud)
在我的研究中,这仅在答案中出现过几次。
它似乎也明确地混淆了其他人,尽管它从未在这个网络上解释过,
Eva*_*oll 11
所以这里的技巧是两个等量递增系列的属性,它们产生可用于识别岛屿的差异{11,12,13} - {1,2,3} = {10,10,10}。此属性不足以识别岛屿本身,但这是我们可以利用的关键步骤。
回避问题。让我们检查一下..在这里我们
这是一些代码。
SELECT x1, x2, x1-x2 AS difference
FROM (VALUES
(2,42),
(13,7),
(42,2)
)
AS xoffsets(o1,o2)
CROSS JOIN LATERAL (
SELECT x+o1,x+o2 -- here we add the offsets above to x
FROM generate_series(1,3) AS t(x)
) AS t(x1, x2)
ORDER BY x1, x2;
x1 | x2 | difference
----+----+------------
3 | 43 | -40
4 | 44 | -40
5 | 45 | -40
14 | 8 | 6
15 | 9 | 6
16 | 10 | 6
43 | 3 | 40
44 | 4 | 40
45 | 5 | 40
(9 rows)
Run Code Online (Sandbox Code Playgroud)
这看起来很不错,difference在这个例子中按 分组就足够了。你可以看到我们在开始三组 (2,42),(13,7)以及(42,2)对应的组xoffsets。这本质上是相反的问题。但是我们有一个主要问题,因为我们正在用静态偏移来证明这一点。如果任何两个偏移量之间的差异o1-o2相同,我们就会遇到问题。像这样,
SELECT x1, x2, x1-x2 AS difference
FROM (VALUES
(100,90),
(90,80)
) AS xoffsets(o1,o2)
CROSS JOIN LATERAL (
SELECT x+o1,x+o2 -- here we add the offsets above to x
FROM generate_series(1,3) AS t(x)
) AS t(x1, x2)
ORDER BY x1, x2;
x1 | x2 | difference
-----+----+------------
91 | 81 | 10
92 | 82 | 10
93 | 83 | 10
101 | 91 | 10
102 | 92 | 10
103 | 93 | 10
Run Code Online (Sandbox Code Playgroud)
我们必须找到一种方法来静态定义第二个偏移量。
SELECT x1, x2, x1-x2 AS difference
FROM (VALUES
(100,0),
(90,0)
) AS xoffsets(o1,o2)
CROSS JOIN LATERAL (
SELECT x+o1,x+o2 -- here we add the offsets above to x
FROM generate_series(1,3) AS t(x)
) AS t(x1, x2)
ORDER BY x1, x2;
x1 | x2 | difference
-----+----+------------
91 | 1 | 90
92 | 2 | 90
93 | 3 | 90
101 | 1 | 100
102 | 2 | 100
103 | 3 | 100
(6 rows)
Run Code Online (Sandbox Code Playgroud)
而且,我们再次回到为每对偏移量创建组的轨道上。这不是我们正在做的,但它非常接近,希望它有助于说明如何减去这两组来创建岛屿。
现在让我们用 table 重新审视上面的问题foo。我们将变量夹在两个副本之间x仅用于显示目的。
SELECT
id,
x,
dense_rank() OVER (w1) AS lhs,
dense_rank() OVER (w2) AS rhs,
dense_rank() OVER (w1)
- dense_rank() OVER (w2)
AS diff,
(
x,
dense_rank() OVER (w1)
- dense_rank() OVER (w2)
) AS grp,
x
FROM foo
WINDOW w1 AS (ORDER BY id),
w2 AS (PARTITION BY x ORDER BY id)
ORDER BY id
LIMIT 10;
id | x | lhs | rhs | diff | grp | x
----+---+-----+-----+------+-------+---
1 | 2 | 1 | 1 | 0 | (2,0) | 2
2 | 1 | 2 | 1 | 1 | (1,1) | 1
3 | 2 | 3 | 2 | 1 | (2,1) | 2
4 | 3 | 4 | 1 | 3 | (3,3) | 3
5 | 3 | 5 | 2 | 3 | (3,3) | 3
6 | 2 | 6 | 3 | 3 | (2,3) | 2
7 | 3 | 7 | 3 | 4 | (3,4) | 3
8 | 1 | 8 | 2 | 6 | (1,6) | 1
9 | 3 | 9 | 4 | 5 | (3,5) | 3
10 | 1 | 10 | 3 | 7 | (1,7) | 1
(10 rows)
Run Code Online (Sandbox Code Playgroud)
除了x和id
lhs是简单的。我们只是用它生成一个唯一的顺序标识符(因为我们给它提供了一个唯一的顺序标识符:id--尽管永远不要忘记ids 很少是无缝的)rhs稍微复杂的。我们x根据不同的x值划分并生成一个顺序标识符。观察rhs每次看到具有它已经看到的值的行时如何在集合上递增。的属性rhs是值被看到的次数。diff是减法的简单结果,但这样想并没有太大用处。想想更像是减去一个序列而不是数字(尽管它们是任何单行的数字)。我们有一个序列,随着一个值出现的次数增加 1。并且,对于每个不同的 id(每次),我们都有一个递增 1 的序列。减去这两个序列将返回相同的重复值数字,就像我们上面的例子一样。(11,12,13) - (1,2,3) = (10,10,10). 这与此答案的第一部分中的原理相同
diff 不单独标记组本身diff强制所有相同的岛x归入一个组(可能有误报,例如上述三种情况diff=3及其对应的x值)群grp是 的函数(x, diff)。它用作分组 id,尽管格式有点奇怪。这有助于减少如果我们只是按差异分组会发生的误报。
所以现在我们有了简单的未优化查询
SELECT x, diff, count(*)
FROM (
SELECT
id,
x,
dense_rank() OVER (ORDER BY id)
- dense_rank() OVER (PARTITION BY x ORDER BY id)
AS diff
FROM foo
) AS t
GROUP BY x, diff;
Run Code Online (Sandbox Code Playgroud)
关于dense_rank()用其他东西替换的问题,比如row_number(). @ypercube 评论
它也适用于 ROW_NUMBER() 但我认为它可能会使用不同的数据给出不同的结果。取决于表中是否有重复数据。
所以让我们回顾一下,这是一个查询,显示
row_number() 和 dense_rank()1,2,3,4,5,6,7,8,6,7,8和xvals 的两个不同“孤岛”的结果集上。SQL查询,
SELECT
id,
x,
dense_rank() OVER (w1) AS dr_w1,
dense_rank() OVER (w2) AS dr_w2,
(
x,
dense_rank() OVER (w1)
- dense_rank() OVER (w2)
) AS dense_diffgrp,
row_number() OVER (w1) AS rn_w1,
row_number() OVER (w2) AS rn_w2,
(
x,
row_number() OVER (w1)
- row_number() OVER (w2)
) AS rn_diffgrp,
x
FROM (
SELECT id,
CASE WHEN id<4
THEN 1
ELSE 0
END
FROM
(
SELECT * FROM generate_series(1,8)
UNION ALL
SELECT * FROM generate_series(6,8)
) AS t(id)
) AS t(id,x)
WINDOW w1 AS (ORDER BY id),
w2 AS (PARTITION BY x ORDER BY id)
ORDER BY id;
Run Code Online (Sandbox Code Playgroud)
结果集、drdense_rank、rnrow_number
id | x | dr_w1 | dr_w2 | dense_diffgrp | rn_w1 | rn_w2 | rn_diffgrp | x
----+---+-------+-------+---------------+-------+-------+------------+---
1 | 1 | 1 | 1 | (1,0) | 1 | 1 | (1,0) | 1
2 | 1 | 2 | 2 | (1,0) | 2 | 2 | (1,0) | 1
3 | 1 | 3 | 3 | (1,0) | 3 | 3 | (1,0) | 1
4 | 0 | 4 | 1 | (0,3) | 4 | 1 | (0,3) | 0
5 | 0 | 5 | 2 | (0,3) | 5 | 2 | (0,3) | 0
6 | 0 | 6 | 3 | (0,3) | 7 | 3 | (0,4) | 0
6 | 0 | 6 | 3 | (0,3) | 6 | 4 | (0,2) | 0
7 | 0 | 7 | 4 | (0,3) | 8 | 6 | (0,2) | 0
7 | 0 | 7 | 4 | (0,3) | 9 | 5 | (0,4) | 0
8 | 0 | 8 | 5 | (0,3) | 10 | 8 | (0,2) | 0
8 | 0 | 8 | 5 | (0,3) | 11 | 7 | (0,4) | 0
Run Code Online (Sandbox Code Playgroud)
您将在此处看到,当您排序的列具有重复项时,您无法使用此方法,因为您无法确保ORDER BY子句中具有重复项的查询的排序。很大程度上只是重复排序的影响导致了差异:全局递增值与分区上递增值的关系。但是,当您有一个唯一的 id 列或一系列定义唯一性的列时,请务必使用row_number()代替dense_rank()。
| 归档时间: |
|
| 查看次数: |
8228 次 |
| 最近记录: |