我有一个表格,我希望能够呈现“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 位”之类的内容。

有帮助吗?

解决方案

如果你想要的排名,这样做

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,

其他提示

不只是这一点:

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

ROW_NUMBER PostgreSQL 中的功能是通过以下方式实现的 LIMIT n OFFSET skip.

编辑:既然你要求 ROW_NUMBER() 而不是简单的排名: row_number() 在 PostgreSQL 8.4 版本中引入。所以你可以考虑更新一下。否则 这个解决方法 可能会有帮助。

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

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

做就是了 :

SELECT count(*) FROM 表 WHERE 分数 > $1

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

或者做:

选择一个。, (选择计数() 来自表 b,其中分数 > b.score) AS 排名 来自表 AS a,其中 pk = ...

但是,如果您选择排名最后的行,那么您将需要计算排名在其之前的所有行,因此您需要扫描整个表,并且速度会非常慢。

解决方案 :

SELECT count(*) FROM (SELECT 1 FROM 表 WHERE 分数 > $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 会执行此操作,因此如果您将统计目标设置为 100 或 1000,则分析,然后运行:

EXPLAIN SELECT * FROM 表 WHERE 分数 > $1

你会得到一个很好的行数估计。

谁需要确切的答案?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top