我有下表:
+---------+------------+----------+-------+
| userId | campaignId | countryId| points|
+---------+------------+----------+-------+
| 10 | 1 | 101 | 72 |
| 3 | 1 | 101 | 30 |
| 6 | 1 | 101 | 72 |
| 4 | 1 | 101 | 49 |
| 1 | 1 | 101 | 53 |
| 8 | 1 | 101 | 67 |
| 5 | 1 | 101 | 6 |
| 7 | 1 | 101 | 87 |
| 2 | 1 | 101 | 41 |
| 11 | 1 | 101 | 76 |
| 9 | 1 | 101 | 50 |
+---------+------------+----------+-------+
我已经用这样的查询创建了一个排行榜:
select
RANK() OVER(order by T.points desc) AS rowRank,
T.UserID, T.points
from table as T
where T.campaignId=@campaignId
OFFSET (@page-1)*@limit ROWS FETCH NEXT @limit ROWS ONLY
上面的查询返回一个从上到下的常规顶部列表。
但是,下一个要求是创建一个排行榜toplist,返回当前用户Id排名+上面2个排名的用户+排名下面2个用户,总共5个用户应该以当前用户为中心列出。
所以额外的输入参数是:
set @userId = 8 // current user where leader board should center around
set @maxTopLimit = 2 // include 2 users ranked above current user
set @maxBottomLimit = 2 // include 2 users ranked below current user
返回的排行榜应如下所示,中间为 userId 8
+---------+------------+----------+-------+---------|
| userId | campaignId | countryId| points| rowRank |
+---------+------------+----------+-------+---------+
| 11 | 1 | 101 | 76 | 3 |
| 10 | 1 | 101 | 72 | 4 |
#####|###### 8 | 1 #########|##### 101 |### 67 |## 5 ####|########
| 9 | 1 | 101 | 50 | 6 |
| 2 | 1 | 101 | 49 | 7 |
+---------+------------+----------+-------+------+--+
我该如何编写这样的 SQL 查询?
答案 0 :(得分:2)
with cte_rank as (...)
from cte_rank cr where cr.UserId = @userId
join cte_rank cr2 on cr2.RowRank >= cr.RowRank - @before and cr2.RowRank <= cr.RowRank + @after
select cr2.*
示例数据
create table CampaignPoints
(
UserId int,
CampaignId int,
CountryId int,
Points int
);
insert into CampaignPoints (UserId, CampaignId, CountryId, Points) values
(10, 1, 101, 72),
( 3, 1, 101, 30),
( 6, 1, 101, 72),
( 4, 1, 101, 49),
( 1, 1, 101, 53),
( 8, 1, 101, 67),
( 5, 1, 101, 6),
( 7, 1, 101, 87),
( 2, 1, 101, 41),
(11, 1, 101, 76),
( 9, 1, 101, 50);
解决方案
declare @userId int = 8;
declare @before int = 2;
declare @after int = 2;
with cte_rank as
(
select cp.UserId,
cp.CampaignId,
cp.CountryId,
cp.Points,
rank() over(order by cp.Points desc) as RowRank
from CampaignPoints cp
)
select cr2.*
from cte_rank cr
join cte_rank cr2
on cr2.RowRank >= cr.RowRank - @before
and cr2.RowRank <= cr.RowRank + @after
where cr.UserId = @userId
order by cr2.RowRank;
结果
UserId CampaignId CountryId Points RowRank
------ ---------- --------- ------ -------
10 1 101 72 3
6 1 101 72 3
8 1 101 67 5
1 1 101 53 6
9 1 101 50 7
Fiddle 查看实际情况。