根据记录在排序结果集中的位置从 mysql table 获取记录的单行号

Get single row number for record from mysql table by its position in sorted result set

我有一个 table,它有两个字段 - id 和 counter。我想按计数器字段对所有记录进行排序,然后按其 id 选择一个记录,并找出它在整个 table 中的位置由其计数器定位。例如:

id counter
1 1
2 5
3 6
4 0
5 4

计数器的 ASC 顺序为:4, 1, 5, 2, 3.

假设我想获取 id=2 的位置,我要查找的值是 4,因为它在结果集中排在第四位。

我找到了一些使用 ROW_NUMBER 方法的解决方案,但这些解决方案似乎都适用于整个结果。我只想通过其 id 为特定行指定一个特定值。

我试图使用一个更简单的解决方案,它将通过在计数器更改时更新 table 来简单地将排名存储在 table 旁边的计数器字段中:

UPDATE mytable 
SET counter_rank = ( SELECT COUNT(*) 
                     FROM mytable 
                     WHERE counter >= ( SELECT counter 
                                        FROM mytable 
                                        WHERE id = ? 
                                        GROUP BY counter)
                                       ) 
                     WHERE id = ?

这是一个很好的解决方案,但它不起作用,因为如果另一个计数器发生变化,则必须计算所有行的排名,这对我的情况来说不是一个好主意。所以我必须看看行号方法的动态解决方案。

还必须考虑按计数器分组 - 多行可以通过具有相同的计数器值来共享排名中的位置。所以不止一行可以 return 相同 number/position.

SELECT id, counter,
       ROW_NUMBER() OVER (ORDER BY counter) position
FROM mytable
/* ORDER BY {needed expression} */
;

对于特定行:

WITH cte AS (
    SELECT id, counter,
           ROW_NUMBER() OVER (ORDER BY counter) position
    FROM mytable
)
SELECT * FROM cte WHERE id = {needed value}
;

如果counter不唯一,则查询不具有确定性,在这种情况下,您必须扩展排序表达式。例如,直到 ORDER BY couner, id.

如果您获得多行及其排名,我会像您发现的那样使用 ROW_NUMBER,但在子查询中。然后 select 来自该子查询的所需行。但是如果你只得到一行,这最容易通过连接计算前面的行来完成:

select mytable.id, mytable.counter, count(t2.id) counter_rank
from mytable
left join mytable t2 on t2.counter < mytable.counter
where mytable.id=2

(如果您想要基于 1 的计数器而不是基于 0 的计数器,则加 1)

fiddle

如果您的版本没有 window 功能,您可以使用这样的查询:

SELECT FIND_IN_SET('2', GROUP_CONCAT(id ORDER BY counter)) AS position
FROM pos 
ORDER BY counter;

样本

MariaDB [test]> select * from pos;
+----+---------+
| id | counter |
+----+---------+
|  1 |       1 |
|  2 |       5 |
|  3 |       6 |
|  4 |       0 |
|  5 |       4 |
+----+---------+
5 rows in set (0.000 sec)

MariaDB [test]> SELECT FIND_IN_SET('2', GROUP_CONCAT(id ORDER BY counter)) AS position
    -> FROM pos 
    -> ORDER BY counter;
+----------+
| position |
+----------+
|        4 |
+----------+
1 row in set (0.000 sec)

MariaDB [test]>