【问题标题】:How to rank users and get a subset from this rank with my user and the above and below user by rank position如何对用户进行排名并从我的用户以及以上和以下用户按排名位置获取此排名的子集
【发布时间】:2013-08-16 16:58:05
【问题描述】:

我现在正在处理一个查询,以获取我的用户排名。我有两张表,一张用于用户,另一张用于利润,我保存金额和与之相关的用户 ID。通过获得一个用户产生的总利润,我需要建立一个包含三个用户的排名,排名靠前的用户对我的用户,我的用户和排名靠后的用户对我的用户。例如:

  id   |            name             | total_profit | rank
-------+-----------------------------+--------------+------
 10312 | John Doe                    |       7000.0 |    1
 10329 | Michael Jordan              |       5000.0 |    2
 10333 | Kobe Bryant                 |       4000.0 |    3
 10327 | Mike Bibby                  |       4000.0 |    3
 10331 | Phil Jackson                |       1000.0 |    4

如果我的用户是科比·布莱恩特,我需要获得迈克尔·乔丹、科比·布莱恩特和菲尔·杰克逊的排名。

如果我的用户是 Mike Bibby,我需要获得 Michale Jordan、Mike Bybby 和 Phil Jackson 的排名。

到目前为止,我有一个查询返回给我所有用户的完整排名,但我现在不知道什么是获得我想要的三个用户的好方法。我曾尝试用 ruby​​ 来做到这一点,但我认为如果我在数据库中进行所有这些处理会更好。

SELECT users.id, users.name, total_profit, rank() OVER(ORDER BY total_profit DESC)
FROM users
INNER JOIN (SELECT sum(profits.amount) AS total_profit, investor_id
          FROM profits GROUP BY profits.investor_id) profits ON profits.investor_id = users.id
ORDER BY total_profit DESC;

我正在使用 PostgresSQL 9.1.4

【问题讨论】:

  • 在 Window Functions 方面做得更好的人可以回答,但我认为 LAG() 和 LEAD() 会得到你想要的。 postgresql.org/docs/9.1/static/functions-window.html
  • 那么你想让科比在你的例子中排名第三还是第二?
  • Kobe bryant 的排名应该是 3,因为他与 Mike Bibby 并列,但在我的最终排名中,我不想显示并列,只想让用户排名更高和排名更低
  • @Eddie 很抱歉,您接受的答案不正确 - 请参阅 sqlfiddle.com/#!1/a2ff5/2
  • @Roman 更新为每个等级只有一个用户。

标签: sql postgresql postgresql-9.1 window-functions


【解决方案1】:
with s as (
    select
        users.id, users.name, total_profit,
        rank() over(order by total_profit desc) as r
    from
        users
        inner join
            (
                select sum(profits.amount) as total_profit,
                investor_id
                from profits
                group by profits.investor_id
            ) profits on profits.investor_id = users.id
), u as (
    select r from s where name = 'Kobe Bryant'
)
select distinct on (r) id, name, total_profit, r
from s
where
    name = 'Kobe Bryant'
    or r in (
        (select r from u) - 1, (select r from u) + 1
    )
order by r;

【讨论】:

    【解决方案2】:
    with cte_profits as (
        select
            sum(p.amount) as total_profit, p.investor_id
        from profits as p
        group by p.investor_id
    ), cte_users_profits as (
        select
            u.id, u.name, p.toral_profit,
            dense_rank() over(order by p.total_profit desc) as rnk,
            row_number() over(partition by up.total_profit order by up.id) as rn
        from users as u
            inner join cte_profits as p on p.investor_id = u.id
    )
    select c2.*
    from cte as c
       left outer join cte as c2 on
           c2.id = c.id or
           c2.rnk = c.rnk + 1 and c2.rn = 1 or
           c2.rnk = c.rnk - 1 and c2.rn = 1
    where c.name = 'Kobe Bryant'
    order by c2.rnk
    

    sql fiddle demo

    【讨论】:

      猜你喜欢
      • 2021-10-27
      • 2021-11-26
      • 2021-09-13
      • 1970-01-01
      • 2010-11-20
      • 2015-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多