Postgresql:如何用n步对组中的记录进行排序
Posted
技术标签:
【中文标题】Postgresql:如何用n步对组中的记录进行排序【英文标题】:Postgresql: how sort records in groups with n step 【发布时间】:2019-05-22 01:53:18 【问题描述】:我的表中有按 rank 排序的行。所有 rank 值都不同。我还有 color 列,其中包含可能的值(红色、蓝色、绿色)。我需要遍历每一行(例如,将其命名为 X)并检查下一个 n 行中是否存在行(例如,将其命名为 Y)其中 X.color == Y.color 然后需要移动 Y 行并将其插入到 X 行之后。 例如 n = 5 和起始表:
id rank color
1 0.8 red
3 0.76 blue
4 0.75 blue
7 0.743 green
5 0.742 red
6 0.741 green
8 0.6 blue
9 0.5 blue
11 0.47 green
12 0.45 blue
10 0.4 red
1 次迭代,我们在接下来的 n 行中有颜色为 red 的行,然后:
id rank color
1 0.8 red
5 0.742 red
3 0.76 blue
4 0.75 blue
7 0.743 green
6 0.741 green
8 0.6 blue
9 0.5 blue
11 0.47 green
12 0.45 blue
10 0.4 red
2 迭代,我们在接下来的 n 行中没有 red,然后没有变化
3 迭代,我们在接下来的 n 行中有 blue,但下一个蓝色行已经在当前行之后。
4 迭代,我们在接下来的 n 行中有 blue,然后:
id rank color
1 0.8 red
5 0.742 red
3 0.76 blue
4 0.75 blue
8 0.6 blue
7 0.743 green
6 0.741 green
9 0.5 blue
11 0.47 green
12 0.45 blue
10 0.4 red
5 次迭代,我们在接下来的 n 行中有 blue,然后:
id rank color
1 0.8 red
5 0.742 red
3 0.76 blue
4 0.75 blue
8 0.6 blue
9 0.5 blue
7 0.743 green
6 0.741 green
11 0.47 green
12 0.45 blue
10 0.4 red
6 迭代,我们在接下来的 n 行中有 blue,然后:
id rank color
1 0.8 red
5 0.742 red
3 0.76 blue
4 0.75 blue
8 0.6 blue
9 0.5 blue
12 0.45 blue
7 0.743 green
6 0.741 green
11 0.47 green
10 0.4 red
7 迭代,我们在接下来的 n 行中有 green,但下一个绿色行已经在当前行之后。
8 迭代,我们在接下来的 n 行中有 green,但下一个绿色行已经在当前行之后。
9 迭代,我们在接下来的 n 行中有 green,但下一个绿色行已经在当前行之后。
10 不进行任何更改。
有人有一个想法如何解决这个问题,我尝试固定窗口函数,但不清楚是否可以在它们的帮助下完成,也许需要一些其他机制?
【问题讨论】:
你说的是“接下来的 n 行”。隐含的排序是rank
的排序吗?当您谈论“移动一行”时,您是什么意思:更改其rank
或在(堆)表中物理移动它?
@LaurenzAlbe 1) 最初按等级排序的记录,在迭代之后,它们没有完全按等级排序 - 如示例 2) “移动一行”意味着重新排序当前请求中的行
您肯定需要为此编写一个函数,因为在每次迭代之后,您都会拥有一个完全不同的数据集。一个简单的查询不能在一次调用中处理不同的数据集。另一种方法可能是尝试使用递归 CTE 的方法,但我不确定这是否可行。函数可能更快
【参考方案1】:
对于 SQL 相对较新的人来说,在遇到此类问题时考虑迭代是很常见的,但无需任何迭代也可以做到这一点。让我们分解一下。首先,让我们根据排名列找到所有数据的row_id:
with data as (select * from (values
(1,0.8 ,'red'),
(3,0.76 ,'blue'),
(4,0.75 ,'blue'),
(7,0.743 ,'green'),
(5,0.742 ,'red'),
(6,0.741 ,'green'),
(8,0.6 ,'blue'),
(9,0.5 ,'blue'),
(11,0.47 ,'green'),
(12,0.45 ,'blue'),
(10,0.4 ,'red')
) v(id, rank, color)
)
select id, rank, color,
row_number() over (order by rank desc) as row_id
FROM data
ORDER BY rank desc;
id | rank | color | row_id
----+-------+-------+--------
1 | 0.8 | red | 1
3 | 0.76 | blue | 2
4 | 0.75 | blue | 3
7 | 0.743 | green | 4
5 | 0.742 | red | 5
6 | 0.741 | green | 6
8 | 0.6 | blue | 7
9 | 0.5 | blue | 8
11 | 0.47 | green | 9
12 | 0.45 | blue | 10
10 | 0.4 | red | 11
(11 rows)
从那里,我们可以找出前五行中颜色相同的最小行:
with data as (select * from (values
(1,0.8 ,'red'),
(3,0.76 ,'blue'),
(4,0.75 ,'blue'),
(7,0.743 ,'green'),
(5,0.742 ,'red'),
(6,0.741 ,'green'),
(8,0.6 ,'blue'),
(9,0.5 ,'blue'),
(11,0.47 ,'green'),
(12,0.45 ,'blue'),
(10,0.4 ,'red')
) v(id, rank, color)
), all_rows as (
select id, rank, color,
row_number() over (order by rank desc) as row_id
FROM data
ORDER BY rank desc
)
select id, rank, color, row_id,
first_value(row_id) over (partition by color order by row_id range between 5
preceding and current row)
from all_rows
order by 5, rank desc;
id | rank | color | row_id | first_value
----+-------+-------+--------+-------------
1 | 0.8 | red | 1 | 1
5 | 0.742 | red | 5 | 1
3 | 0.76 | blue | 2 | 2
4 | 0.75 | blue | 3 | 2
8 | 0.6 | blue | 7 | 2
9 | 0.5 | blue | 8 | 3
7 | 0.743 | green | 4 | 4
6 | 0.741 | green | 6 | 4
11 | 0.47 | green | 9 | 4
12 | 0.45 | blue | 10 | 7
10 | 0.4 | red | 11 | 11
(11 rows)
这很接近,但并不完全正确。我们需要从每种颜色的最后 5 行中获取最小值中的最小值。我们还需要一步:
with data as (select * from (values
(1,0.8 ,'red'),
(3,0.76 ,'blue'),
(4,0.75 ,'blue'),
(7,0.743 ,'green'),
(5,0.742 ,'red'),
(6,0.741 ,'green'),
(8,0.6 ,'blue'),
(9,0.5 ,'blue'),
(11,0.47 ,'green'),
(12,0.45 ,'blue'),
(10,0.4 ,'red')
) v(id, rank, color)
), all_rows as (
select id, rank, color,
row_number() over (order by rank desc) as row_id
FROM data
ORDER BY rank desc
), first_values as (
select id, rank, color, row_id,
first_value(row_id) over (partition by color order by row_id range between 5
preceding and current row)
from all_rows
order by 5, rank desc
)
select id, rank, color, row_id,
first_value(first_value) over (partition by color order by row_id range between 5 preceding and current row) as overall_rank
FROM first_values
ORDER BY overall_rank, rank desc
;
id | rank | color | row_id | overall_rank
----+-------+-------+--------+--------------
1 | 0.8 | red | 1 | 1
5 | 0.742 | red | 5 | 1
3 | 0.76 | blue | 2 | 2
4 | 0.75 | blue | 3 | 2
8 | 0.6 | blue | 7 | 2
9 | 0.5 | blue | 8 | 2
12 | 0.45 | blue | 10 | 2
7 | 0.743 | green | 4 | 4
6 | 0.741 | green | 6 | 4
11 | 0.47 | green | 9 | 4
10 | 0.4 | red | 11 | 11
(11 rows)
【讨论】:
以上是关于Postgresql:如何用n步对组中的记录进行排序的主要内容,如果未能解决你的问题,请参考以下文章