Select 每个人的前 4 名得分,但至少需要两个位置
Select top 4 scores by person but need at least two locations
我有这样的数据
eventId locationId score athlete
8739 73 48 matt
8734 73 46 matt
8788 73 45 matt
8738 73 44 matt
8787 73 44 matt
8735 73 43 matt
8789 6 43 matt
我需要按人获取前 4 个分数,但前 4 个分数中至少有 1 个需要来自与其他 3 个不同的 locationId
在这种情况下,我想要这个 returned
eventId locationId score athlete
8739 73 48 matt
8734 73 46 matt
8788 73 45 matt
8789 6 43 matt
我试过写出使用 GROUP BY HAVING MIN(locationId) != MAX(locationId)
的查询,但我不确定如何在使用 ORDER BY
和 LIMIT
的同时完成它。
我也尝试过自连接,但我不确定如何根据 s.score
和 score2
.[=20= return 获得最佳结果]
似乎在正确轨道上的自我加入的开始
SELECT s.eventid, s.locationid, athlete, score
, s2.eventid, s2.locationid, s2.athlete, score score2
FROM singles s
INNER JOIN singles s2 ON s.athlete = s2.athlete AND s.locationid != s2.locationid
WHERE s.athlete = 'matt'
ORDER BY score DESC;
您可以使用 row_number
分析函数和 limit
子句,其中包括一个 self-join
,如下所示
select locationId, score, athlete
from
(
select locationId, score, athlete, rn, rn2
from(
select *
from
(
with singles(locationId, score, athlete) as
(
select 73, 48, 'matt' union all
select 73, 46, 'matt' union all
select 73, 45, 'matt' union all
select 73, 44, 'matt' union all
select 73, 44, 'matt' union all
select 73, 43, 'matt' union all
select 6, 43, 'matt'
)
select row_number() over (partition by s.locationId order by s.score desc) as rn,
row_number() over (partition by s2.locationId order by s2.score desc) as rn2,
s.athlete, s.locationId, s.score
from singles s join singles s2
on s.score = s2.score
where s.athlete = 'matt'
) q1
order by score desc, rn, rn2
) q2
group by locationId, score
having sum(rn) <= sum(rn2)
order by rn, score desc
limit 4
) q3
order by score desc
所以,你真正想要的是前三名,然后是保证至少两个位置的第一个分数。
这是一个相当困难的条件,但我认为这可以解决问题:
with s as (
select t.*,
row_number() over (partition by athlete order by score desc) as seqnum
from t
),
s3 as (
select s.*
from s
where seqnum <= 3
)
select *
from s3
union all
(select s.*
from s
where ( (select count(distinct locationid) from s3) > 1 and seqnum = 4 ) or
( (select count(distinct locationid) from s3) = 1 and
seqnum = (select min(seqnum)
from s
where locationid not in (select locationid from s3)
)
)
);
Here 是一个 db<>fiddle.
我有这样的数据
eventId locationId score athlete
8739 73 48 matt
8734 73 46 matt
8788 73 45 matt
8738 73 44 matt
8787 73 44 matt
8735 73 43 matt
8789 6 43 matt
我需要按人获取前 4 个分数,但前 4 个分数中至少有 1 个需要来自与其他 3 个不同的 locationId
在这种情况下,我想要这个 returned
eventId locationId score athlete
8739 73 48 matt
8734 73 46 matt
8788 73 45 matt
8789 6 43 matt
我试过写出使用 GROUP BY HAVING MIN(locationId) != MAX(locationId)
的查询,但我不确定如何在使用 ORDER BY
和 LIMIT
的同时完成它。
我也尝试过自连接,但我不确定如何根据 s.score
和 score2
.[=20= return 获得最佳结果]
似乎在正确轨道上的自我加入的开始
SELECT s.eventid, s.locationid, athlete, score
, s2.eventid, s2.locationid, s2.athlete, score score2
FROM singles s
INNER JOIN singles s2 ON s.athlete = s2.athlete AND s.locationid != s2.locationid
WHERE s.athlete = 'matt'
ORDER BY score DESC;
您可以使用 row_number
分析函数和 limit
子句,其中包括一个 self-join
,如下所示
select locationId, score, athlete
from
(
select locationId, score, athlete, rn, rn2
from(
select *
from
(
with singles(locationId, score, athlete) as
(
select 73, 48, 'matt' union all
select 73, 46, 'matt' union all
select 73, 45, 'matt' union all
select 73, 44, 'matt' union all
select 73, 44, 'matt' union all
select 73, 43, 'matt' union all
select 6, 43, 'matt'
)
select row_number() over (partition by s.locationId order by s.score desc) as rn,
row_number() over (partition by s2.locationId order by s2.score desc) as rn2,
s.athlete, s.locationId, s.score
from singles s join singles s2
on s.score = s2.score
where s.athlete = 'matt'
) q1
order by score desc, rn, rn2
) q2
group by locationId, score
having sum(rn) <= sum(rn2)
order by rn, score desc
limit 4
) q3
order by score desc
所以,你真正想要的是前三名,然后是保证至少两个位置的第一个分数。
这是一个相当困难的条件,但我认为这可以解决问题:
with s as (
select t.*,
row_number() over (partition by athlete order by score desc) as seqnum
from t
),
s3 as (
select s.*
from s
where seqnum <= 3
)
select *
from s3
union all
(select s.*
from s
where ( (select count(distinct locationid) from s3) > 1 and seqnum = 4 ) or
( (select count(distinct locationid) from s3) = 1 and
seqnum = (select min(seqnum)
from s
where locationid not in (select locationid from s3)
)
)
);
Here 是一个 db<>fiddle.