6

我有一张表,我希望能够为其呈现“Y 中排名 X”的数据。特别是,我希望能够以相对有效的方式呈现单个行的数据(即不选择表中的每一行)。排名本身非常简单,它是表中单个列的直接 ORDER BY。

Postgres 似乎在这方面提出了一些独特的挑战。AFAICT 它没有 RANK 或 ROW_NUMBER 或等效功能(至少在 8.3 中,我暂时坚持使用它)。邮件列表档案中的规范答案似乎是创建一个临时序列并从中选择:

test=> create temporary sequence tmp_seq;
CREATE SEQUENCE
test=*> select nextval('tmp_seq') as row_number, col1, col2 from foo;

当我只想从表中选择一行时(并且我想按 PK 选择它,而不是按排名),这个解决方案似乎仍然无济于事。

我可以将排名非规范化并将其存储在单独的列中,这使得呈现数据变得微不足道,但只是重新定位了我的问题。UPDATE 不支持 ORDER BY,所以我不确定如何构建一个 UPDATE 查询来设置排名(没有选择每一行并为每一行运行一个单独的 UPDATE,这似乎太多的数据库活动每次队伍需要更新时触发)。

我错过了一些明显的东西吗?这样做的正确方法是什么?

编辑:显然我不够清楚。我知道 OFFSET/LIMIT,但我不明白它如何帮助解决这个问题。我不是想选择排名第 X 的项目,而是尝试选择任意项目(例如,通过其 PK),然后能够向用户显示“在 312 中排名第 43”之类的内容。

4

4 回答 4

7

如果您想要排名,请执行以下操作

SELECT id,num,rank FROM (
  SELECT id,num,rank() OVER (ORDER BY num) FROM foo
) AS bar WHERE id=4

或者,如果您确实想要行号,请使用

SELECT id,num,row_number FROM (
  SELECT id,num,row_number() OVER (ORDER BY num) FROM foo
) AS bar WHERE id=4

当您在某处具有相同的值时,它们会有所不同。如果需要,还可以使用 dense_rank()。

当然,这需要 PostgreSQL 8.4。

于 2009-09-23T18:14:02.570 回答
5

不就是这样吗:

SELECT  *
FROM    mytable
ORDER BY
        col1
OFFSET X LIMIT 1

或者我错过了什么?

更新:

如果要显示排名,请使用以下命令:

SELECT  mi.*, values[1] AS rank, values[2] AS total
FROM    (
        SELECT  (
                SELECT  ARRAY[SUM(((mi.col1, mi.ctid) < (mo.col1, mo.ctid))::INTEGER), COUNT(*)]
                FROM    mytable mi
                ) AS values
        FROM    mytable mo
        WHERE   mo.id = @myid
        ) q
于 2009-09-23T15:29:47.090 回答
3

ROW_NUMBERPostgreSQL 中的功能是通过LIMIT n OFFSET skip.

编辑:由于您要求ROW_NUMBER()而不是简单的排名:row_number()在 8.4 版中被引入 PostgreSQL。所以你可能会考虑更新。否则,此解决方法可能会有所帮助。

于 2009-09-23T15:29:46.630 回答
1

以前的回复解决了“选择所有行并获得他们的排名”的问题,这不是你想要的......

  • 你有一排
  • 你想知道它的排名

做就是了 :

SELECT count(*) FROM table WHERE score > $1

其中 $1 是您刚刚选择的行的分数(我想您想显示它以便您可以选择它......)。

或者做:

选择一个。, (SELECT count( ) FROM table b WHERE score > b.score) AS rank FROM table as a WHERE pk = ...

但是,如果您选择排在最后的行,是的,您需要计算排在它之前的所有行,因此您需要扫描整个表,而且速度会很慢。

解决方案 :

SELECT count(*) FROM (SELECT 1 FROM table WHERE score > $1 LIMIT 30)

您将获得 30 个最佳分数的精确排名,而且速度很快。谁在乎失败者?

好的,如果你真的关心失败者,你需要制作一个直方图:

假设得分可以从 0 到 100,并且您有 1000000 个得分 < 80 的失败者和 10 个得分 > 80 的获胜者。

你制作一个有多少行得分为 X 的直方图,它是一个有 100 行的简单小表。向主表添加触发器以更新直方图。

现在,如果你想对得分为 X 的失败者进行排名,他的排名是 sum( histo ),其中 histo_score > X。

由于您的分数可能不在 0 到 100 之间,而是(例如)在 0 到 1000000000 之间,因此您需要稍微捏造一下,例如扩大直方图箱。所以你最多只需要 100 个 bin,或者使用一些对数直方图分布函数。

顺便说一句,当你分析表时 postgres 会这样做,所以如果你将 statistics_target 设置为 100 或 1000 的分数,分析,然后运行:

EXPLAIN SELECT * FROM table WHERE score > $1

你会得到一个不错的行数估计。

谁需要确切的答案?

于 2009-09-26T17:52:03.753 回答