SQL Oracle 摘要 table 查询每个组的前 n 个
SQL Oracle summary table query with top-n on each group
我有两个 table 这样描述的
交易Table
客户 ID | fk 给客户 table,
金额 |每笔交易花费的金额
客户Table
客户 ID | ,
年龄 |客户年龄
我需要做
按年龄范围对客户进行分组或分桶的查询
我需要让每个年龄组的前 3 名金额最高,也是后 3 名或最低消费者
到目前为止我所做的是
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=ccf988ec525dc133cf7825ba052e8054
what i manage so far
inner query
我已经尝试使用 CTE,但仍然不确定如何在每组中的每个 group/bottom 3 个中获得前 3 名
with cte
问题出在第一行的第一张图片上,return 超过 3/他们附加了所有记录,而我只需要前 3
感谢您的帮助
您可以使用 ROW_NUMBER()
分析函数找到最高和最低,然后您可以在 LISTAGG
中使用条件聚合仅显示相应列表中的最高和最低:
select age_bucket,
LISTAGG(CASE WHEN max_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount DESC) AS TOP_3_AMOUNT,
LISTAGG(CASE WHEN min_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount ASC ) AS BOTTOM_3_AMOUNT
FROM (
SELECT amount,
age_bucket,
ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount DESC) AS max_rn,
ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount ASC ) AS min_rn
FROM (
select t.amount,
case
when c.age <= 20 THEN '<= 20'
when c.age between 21 and 30 THEN '21-30'
when c.age between 31 and 40 THEN '31-40'
when c.age between 41 and 50 THEN '41-50'
when c.age >= 51 THEN '>= 51'
END as age_bucket
from transaction t
join customer c on t.cust_id=c.cust_id
)
)
WHERE max_rn <= 3
OR min_rn <= 3
group by age_bucket
order by age_bucket asc;
db<>fiddle here
I need to make it only top 3 or bottom 3 distinct by the customer id
select age_bucket,
LISTAGG(CASE WHEN max_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount DESC) AS TOP_3_AMOUNT,
LISTAGG(CASE WHEN min_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount ASC ) AS BOTTOM_3_AMOUNT
FROM (
SELECT amount,
age_bucket,
CASE max_cust_rn
WHEN 1
THEN ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount DESC)
END AS max_rn,
CASE min_cust_rn
WHEN 1
THEN ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount ASC )
END AS min_rn
FROM (
SELECT amount,
age_bucket,
ROW_NUMBER() OVER (
PARTITION BY age_bucket, cust_id ORDER BY amount DESC) AS max_cust_rn,
ROW_NUMBER() OVER (
PARTITION BY age_bucket, cust_id ORDER BY amount ASC ) AS min_cust_rn
FROM (
select t.amount,
t.cust_id,
case
when c.age <= 20 THEN '<= 20'
when c.age between 21 and 30 THEN '21-30'
when c.age between 31 and 40 THEN '31-40'
when c.age between 41 and 50 THEN '41-50'
when c.age >= 51 THEN '>= 51'
END as age_bucket
from transaction t
join customer c on t.cust_id=c.cust_id
)
)
WHERE max_cust_rn = 1
OR min_cust_rn = 1
)
WHERE max_rn <= 3
OR min_rn <= 3
group by age_bucket
order by age_bucket asc;
db<>fiddle here
因为没有表的说明,所以就试着改了一点
你能试试吗:
SELECT age_bucket,
Listagg(
CASE
WHEN seqnumbyeachcusttop = 1 THEN amount
END, ',') within GROUP (ORDER BY amount DESC) AS test_top_3_amouny,
listagg(amount, ',') within GROUP (ORDER BY amount DESC) AS top_3_principal
FROM (
SELECT age_bucket,
seqnumbyeachcusttop,
amount,
row_number() OVER (partition BY amount ORDER BY amount DESC) rn
FROM (
SELECT t.amount AS amount,
t.cust_id AS cust_id,
row_number() OVER (partition BY t.cust_id ORDER BY t.amount DESC) AS seqnumbyeachcusttop,
CASE
WHEN c.age <= 20 THEN ' <= 20'
WHEN c.age BETWEEN 21 AND 30 THEN '21-30'
WHEN c.age BETWEEN 31 AND 40 THEN '31-40'
WHEN c.age BETWEEN 41 AND 50 THEN '41-50'
WHEN c.age >= 51 THEN '>= 51'
END AS age_bucket
FROM TRANSACTION t
JOIN customer c
ON t.cust_id=c.cust_id ))
WHERE rn <= 3) GROUP BY age_bucket ORDER BY age_bucket ASC;
我有两个 table 这样描述的
交易Table 客户 ID | fk 给客户 table, 金额 |每笔交易花费的金额
客户Table 客户 ID | , 年龄 |客户年龄
我需要做 按年龄范围对客户进行分组或分桶的查询 我需要让每个年龄组的前 3 名金额最高,也是后 3 名或最低消费者 到目前为止我所做的是 https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=ccf988ec525dc133cf7825ba052e8054
what i manage so far inner query
我已经尝试使用 CTE,但仍然不确定如何在每组中的每个 group/bottom 3 个中获得前 3 名 with cte
问题出在第一行的第一张图片上,return 超过 3/他们附加了所有记录,而我只需要前 3
感谢您的帮助
您可以使用 ROW_NUMBER()
分析函数找到最高和最低,然后您可以在 LISTAGG
中使用条件聚合仅显示相应列表中的最高和最低:
select age_bucket,
LISTAGG(CASE WHEN max_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount DESC) AS TOP_3_AMOUNT,
LISTAGG(CASE WHEN min_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount ASC ) AS BOTTOM_3_AMOUNT
FROM (
SELECT amount,
age_bucket,
ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount DESC) AS max_rn,
ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount ASC ) AS min_rn
FROM (
select t.amount,
case
when c.age <= 20 THEN '<= 20'
when c.age between 21 and 30 THEN '21-30'
when c.age between 31 and 40 THEN '31-40'
when c.age between 41 and 50 THEN '41-50'
when c.age >= 51 THEN '>= 51'
END as age_bucket
from transaction t
join customer c on t.cust_id=c.cust_id
)
)
WHERE max_rn <= 3
OR min_rn <= 3
group by age_bucket
order by age_bucket asc;
db<>fiddle here
I need to make it only top 3 or bottom 3 distinct by the customer id
select age_bucket,
LISTAGG(CASE WHEN max_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount DESC) AS TOP_3_AMOUNT,
LISTAGG(CASE WHEN min_rn <= 3 THEN amount END, ',')
WITHIN GROUP (ORDER BY amount ASC ) AS BOTTOM_3_AMOUNT
FROM (
SELECT amount,
age_bucket,
CASE max_cust_rn
WHEN 1
THEN ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount DESC)
END AS max_rn,
CASE min_cust_rn
WHEN 1
THEN ROW_NUMBER() OVER (PARTITION BY age_bucket ORDER BY amount ASC )
END AS min_rn
FROM (
SELECT amount,
age_bucket,
ROW_NUMBER() OVER (
PARTITION BY age_bucket, cust_id ORDER BY amount DESC) AS max_cust_rn,
ROW_NUMBER() OVER (
PARTITION BY age_bucket, cust_id ORDER BY amount ASC ) AS min_cust_rn
FROM (
select t.amount,
t.cust_id,
case
when c.age <= 20 THEN '<= 20'
when c.age between 21 and 30 THEN '21-30'
when c.age between 31 and 40 THEN '31-40'
when c.age between 41 and 50 THEN '41-50'
when c.age >= 51 THEN '>= 51'
END as age_bucket
from transaction t
join customer c on t.cust_id=c.cust_id
)
)
WHERE max_cust_rn = 1
OR min_cust_rn = 1
)
WHERE max_rn <= 3
OR min_rn <= 3
group by age_bucket
order by age_bucket asc;
db<>fiddle here
因为没有表的说明,所以就试着改了一点 你能试试吗:
SELECT age_bucket,
Listagg(
CASE
WHEN seqnumbyeachcusttop = 1 THEN amount
END, ',') within GROUP (ORDER BY amount DESC) AS test_top_3_amouny,
listagg(amount, ',') within GROUP (ORDER BY amount DESC) AS top_3_principal
FROM (
SELECT age_bucket,
seqnumbyeachcusttop,
amount,
row_number() OVER (partition BY amount ORDER BY amount DESC) rn
FROM (
SELECT t.amount AS amount,
t.cust_id AS cust_id,
row_number() OVER (partition BY t.cust_id ORDER BY t.amount DESC) AS seqnumbyeachcusttop,
CASE
WHEN c.age <= 20 THEN ' <= 20'
WHEN c.age BETWEEN 21 AND 30 THEN '21-30'
WHEN c.age BETWEEN 31 AND 40 THEN '31-40'
WHEN c.age BETWEEN 41 AND 50 THEN '41-50'
WHEN c.age >= 51 THEN '>= 51'
END AS age_bucket
FROM TRANSACTION t
JOIN customer c
ON t.cust_id=c.cust_id ))
WHERE rn <= 3) GROUP BY age_bucket ORDER BY age_bucket ASC;