当必须根据条件对记录进行分组时如何选择最多 x 行
Posted
技术标签:
【中文标题】当必须根据条件对记录进行分组时如何选择最多 x 行【英文标题】:How to select up to x rows when records have to be grouped based on criteria 【发布时间】:2020-01-17 08:09:47 【问题描述】:我想创建查询,最多选择 x 行,记录按一个 id 分组,整个组必须是查询的结果。我基于过滤的值存储在 p_id 列中,具有相同值的行创建组。 如果是该表:
p_id age
0 00170 64
1 00170 64
2 00201 24
3 00201 64
4 00201 64
5 00300 24
6 00300 20
我想选择 4 行,但是因为 p_id 为 00170,00201 的组总共有 5 条记录我得到:
0 00170 64
1 00170 64
如果我选择 5 行,我会得到:
0 00170 64
1 00170 64
2 00201 24
3 00201 64
4 00201 64
如果我选择 6 行,我会得到(p_id 00300 是 2 条记录,因此不包括在内,总和超过 6):
0 00170 64
1 00170 64
2 00201 24
3 00201 64
4 00201 64
因此返回整个组。我正在使用 oracle db,使用 ROWNUM 选择 x 行很容易。当我尝试使用附加条件达到一定数量的行时,我会迷路。
【问题讨论】:
到目前为止你尝试了什么,你能分享一些你的代码吗?不要使用rownum来限制行数见***.com/questions/470542/… @Hrzug 。 . .从您的问题中不清楚是否订购了p_id
值。或者,如果有另一列对它们进行排序。也就是说,您总是想要p_id
的最小值,还是想要基于表中其他列的“最早”。
【参考方案1】:
Oracle 设置:
CREATE TABLE test_data ( p_id, age ) AS
SELECT '00170', 64 FROM DUAL UNION ALL
SELECT '00170', 64 FROM DUAL UNION ALL
SELECT '00201', 24 FROM DUAL UNION ALL
SELECT '00201', 64 FROM DUAL UNION ALL
SELECT '00201', 64 FROM DUAL UNION ALL
SELECT '00300', 24 FROM DUAL UNION ALL
SELECT '00300', 20 FROM DUAL;
查询:
对行排序,然后找到每个组的最大行号,然后过滤以仅返回最大行号包含在您想要的行限制中的组:
SELECT p_id,
age
FROM (
SELECT t.*,
MAX( ROWNUM ) OVER ( PARTITION BY p_id ) AS grp
FROM (
SELECT *
FROM test_data
ORDER BY p_id
) t
)
WHERE grp <= 4;
输出:
P_ID |年龄 :---- | --: 00170 | 64 00170 | 64
如果将最后一行更改为 5 则返回 5 行并将其更改为 6 则仍将返回 5 行。
db小提琴here
【讨论】:
【参考方案2】:我会通过窗口计数和过滤来解决这个问题:
select p_id, age
from (select p_id, age, count(*) over(order by p_id) cnt from mytable t) t
where cnt <= 5
order by p_id
您可以根据需要更改cnt <= 5
。
Demo on DB Fiddle:
cnt <= 4
:
cnt <= 5
:
cnt <= 6
:
【讨论】:
【参考方案3】:GMB 的回答很好。但可以通过使用RANK()
稍微简化一下。这个函数恰好完全做你想做的事:
select p_id, age
from (select t.*,
rank() over (order by p_id) as rnk
from t
) t
where rnk <= 5
order by p_id;
不过,更重要的是,如果 p_id
值没有排序,那么您可能需要一个额外的步骤:将某个排序列的最小值分配给每个 p_id
。让我叫那个订购栏id
:
select p_id, age
from (select t.*,
rank() over (order by p_id_grp) as rnk
from (select t.*, min(id) over (partition by p_id) as p_id_grp
from t
) t
) t
where rnk <= 5
order by p_id;
【讨论】:
【参考方案4】:这是一个典型的 Top-N 查询:
使用带有有序视图的 ROWNUM 来获得正确的排序:
SELECT p_id, age
FROM (SELECT p_id, age
FROM table
ORDER BY age DESC)
WHERE ROWNUM <= 4;
对于 Oracle v 12c 及以后的版本,有新的 FETCH 子句:
SELECT p_id, age
FROM table
GROUP BY p_id
FETCH FIRST 4 ROWS ONLY;
更多资源:https://oracle-base.com/articles/misc/top-n-queries
【讨论】:
根据 OP 的意图,这不是正确的解决方案。以上是关于当必须根据条件对记录进行分组时如何选择最多 x 行的主要内容,如果未能解决你的问题,请参考以下文章