根据记录在排序结果集中的位置从 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)
如果您的版本没有 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]>
我有一个 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)
如果您的版本没有 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]>