使用row_number反向排名查询

时间:2014-10-21 16:46:09

标签: sql postgresql coalesce window-functions

我有一个查询,我对返回的行进行排名(related question)。我希望能够做的是反转排名,并相应地设置new_rank。让我们假设我有999个条目。

例如,如果我有这个查询:

with persondata as (
   SELECT firstname,
          lastname,
          year,
          personid,
        ((SELECT COALESCE(SUM(thevalue),0)
          FROM assets
          WHERE personidref = w.personid AND year = w.year)
       - (SELECT COALESCE(SUM(amount),0)
          FROM liabilities
          WHERE personidref = w.personid AND year = w.year)) as worth,
          row_number() over(ORDER BY w.worth DESC) as new_rank,
         visible
   FROM members w 
   WHERE year = 2014 
   AND visible = 1 
   ORDER BY worth ASC LIMIT 5
) 
select row_number() over (order by worth DESC) as rank,
       * 
from persondata

这正确地以所需顺序返回底部5,见下文:

 rank | firstname |     lastname      |  worth   | new_rank
------+-----------+-------------------+----------+----------
    1 | Peter     | Griffin           |  -520000 |      145 
    2 | Steve     | Moffat            |  -530000 |      519 
    3 | Gregory   | Peck              |  -540000 |      131 
    4 | Ruben     | Mumbles           |  -550000 |      130 
    5 | Ricky     | Swiss             |  -560000 |      120

有没有办法合并row_count并减去当前行数?或者设置一个暂时使用的计数器,设置为行计数然后递减?

最终预期结果:

 rank | firstname |     lastname      |  worth   | new_rank 
------+-----------+-------------------+----------+----------
  997 | Peter     | Griffin           |  -520000 |      145 
  996 | Steve     | Moffat            |  -530000 |      519 
  997 | Gregory   | Peck              |  -540000 |      131 
  998 | Ruben     | Mumbles           |  -550000 |      130 
  999 | Ricky     | Swiss             |  -560000 |      120 

使用row_number时,我无法找到有关此实现的任何内容。

1 个答案:

答案 0 :(得分:2)

问题

难怪你被这个查询困住了。您的格式和命名约定是混淆方式(格式已经被编辑)。

  • 您基于new_rank计算members.worth 非常混乱,然后计算新的worth(重新使用相同的名称! )和rank基于此。

    • 不要重复使用相同的名称“值”。使用单独的,有意义的标识符。
    • 为什么要根据值“new_rank”调用排名?
  • 为什么要开始使用row_number()代替rank()来计算“排名”?

  • 为什么要为表w使用别名members

  • 您的ORDER BY worth使用新计算的worth,而非members.worth。你知道吗?构建这样的陷阱是非常不明智的,除非你实际上希望混淆你的代码。

  • 如果您想要两个排名,则CTE (Common Table Expression)中不能LIMIT 5。要获得相对的地位,您必须为所有相关行计算新的worth。但是,目前还不清楚您的排名实际上哪些行相关members中的所有行?只是那些通过WHERE条件的人?

  • 最后,未定义如何打破关系。如果第3到第7高计算worth相同,那么您期望得到什么?

解决方案

考虑SELECT查询中的事件序列:

我根本不会在这里使用CTE。它不是必需的,可能比子查询慢。在加入之前总计每人的资产和负债总和。这样,您就可以避免乘以行:

我使用calc_worthcalc_rank代替输出列worthrank来解开命名。

SELECT count(*) OVER ()     -- AS total_ct
       + 1                  -- counter off-by-1 error
       - rank() OVER (ORDER BY COALESCE(a.asset_sum, 0)
                             - COALESCE(l.liab_sum, 0)) AS calc_rank
     , m.firstname, m.lastname
     , COALESCE(a.asset_sum, 0) - COALESCE(l.liab_sum, 0) AS calc_worth
     , rank() OVER (ORDER BY m.worth DESC) AS rank
FROM  members m
LEFT  JOIN (
   SELECT personidref, sum(thevalue) AS asset_sum
   FROM   assets
   WHERE  year = 2014
   GROUP  BY 1
   ) a ON a.personidref = m.personid
LEFT JOIN (
   SELECT personidref, sum(amount) AS liab_sum
   FROM   liabilities
   WHERE  year = 2014
   ) l ON l.personidref = m.personid
WHERE  m.year = 2014 
AND    m.visible = 1 
ORDER  BY calc_worth DESC
LIMIT  5;

如果要反转结果中的排序顺序,请将其包装在子查询中,并在外部查询中再次排序。