2013-08-16 2 views
2

Я сейчас работаю над запросом, чтобы получить рейтинг моих пользователей. У меня есть две таблицы для пользователей, а другая для прибыли, где я сохраняю сумму и идентификатор пользователя, к которому относится. Получая общую прибыль, получаемую пользователем, мне нужно создать ранг с тремя пользователями, пользователь в следующем более ранжированном положении для моего пользователя, моего пользователя и пользователя в следующей ниже ранговой позиции для моего пользователя. Например:Как ранжировать пользователей и получать подмножество из этого ранга с моим пользователем и выше и ниже пользователем по рангу позиции

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 

В этом случае мой пользователь Kobe Bryant я должен был бы получить звание с Майкл Джордан, Кобе Брайант и Фил Джексон.

Если мой пользователь - Майк Бибби, мне нужно будет получить звание Михаила Джордана, Майка Бэбби и Фила Джексона.

До сих пор у меня есть запрос, который возвращает мне полный ранг со всеми пользователями, но теперь я не знаю, что является хорошим способом получить трех пользователей, которых я хочу. Я попытался сделать это с помощью 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

+1

Кто лучше функций окна может ответить, но я думаю, что LAG() и LEAD() будет то, что вы получите после. http://www.postgresql.org/docs/9.1/static/functions-window.html – bma

+0

, так что вы хотите, чтобы Коби Брайант в вашем примере имел ранг 3 или 2? –

+0

Ранг Коби Брайанта должен быть 3, потому что он связан с Майком Бибби, но в моем последнем звании я не хочу показывать галстуки и хочу только получить пользователя с более высоким рангом и с более низким рангом – Eddie

ответ

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; 
+0

этот работает некорректно, см. Http://sqlfiddle.com/#!1/a2ff5/2 –

+0

@Roman Обновлено. –

0
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