MySQL在ORDER BY中获得行位置


86

用下面的MySQL表:

+-----------------------------+
+ id INT UNSIGNED             +
+ name VARCHAR(100)           +
+-----------------------------+

当按排序时,如何选择单行及其在表中其他行中的位置name ASC。因此,如果表数据看起来像这样,按名称排序时:

+-----------------------------+
+ id | name                   +
+-----------------------------+
+  5 | Alpha                  +
+  7 | Beta                   +
+  3 | Delta                  +
+ .....                       +
+  1 | Zed                    +
+-----------------------------+

如何选择Beta获得该行当前位置的行?我正在寻找的结果集将是这样的:

+-----------------------------+
+ id | position | name        +
+-----------------------------+
+  7 |        2 | Beta        +
+-----------------------------+

我可以做一个简单的操作,SELECT * FROM tbl ORDER BY name ASC然后枚举PHP中的行,但是仅为单个行加载可能较大的结果集似乎很浪费。



Answers:


120

用这个:

SELECT x.id, 
       x.position,
       x.name
  FROM (SELECT t.id,
               t.name,
               @rownum := @rownum + 1 AS position
          FROM TABLE t
          JOIN (SELECT @rownum := 0) r
      ORDER BY t.name) x
 WHERE x.name = 'Beta'

...以获得唯一的位置值。这个:

SELECT t.id,
       (SELECT COUNT(*)
          FROM TABLE x
         WHERE x.name <= t.name) AS position,
       t.name    
  FROM TABLE t      
 WHERE t.name = 'Beta'

...将赋予领带相同的价值。IE:如果第二个值有两个,则当第一个查询将其中一个赋予2的位置,将另一个赋予3的位置时,它们的位置均为2。


@actual:有什么可说的-有没有其他选择,除了移动的竞争对手,它支持解析函数(PostgreSQL的,甲骨文,SQL服务器,DB2 ...)
OMG小马

2
@OMGPonies只是忘了逗号position,但是很完美。
Pierallard 2014年

我知道这是一篇很老的文章,但是我需要一个类似的解决方案。但是,当使用内部联接,分组依据和排序依据时,“位置”字段将忽略这些联接,并且值将全部混合在一起。有什么办法吗?
丹尼尔(Daniel)

@Daniel您应该提出一个新问题,也许可以参考这个问题。
PeerBr

@actual,因为这是对单行的查询,所以这里不必担心性能。如果要获取完整列表的排名,则有所不同,但是您可以“欺骗”并通过按点排序来使用隐式排名。
AgmLauncher 2015年

20

这是我能想到的唯一方法:

SELECT `id`,
       (SELECT COUNT(*) FROM `table` WHERE `name` <= 'Beta') AS `position`,
       `name`
FROM `table`
WHERE `name` = 'Beta'

2
+1不错的技巧...但是,您可能想name <= 'Beta'改用
Daniel Vassallo'9

这种方法将给position领带带来相同的价值。
OMG小马

(删除了我之前的评论-我错了)...如果LIMIT 1在其中添加怎么办?如果是领带,您将只获得领带最后位置的一行。
丹尼尔·瓦萨洛

如果OP可以保证该name字段是唯一的-那么就没有理由使查询变得更加复杂。如果他不能-那么让我们等待他对并列姓名的预期结果。
zerkms's

8

如果查询很简单并且返回的结果集的大小可能很大,则可以尝试将其拆分为两个查询。

具有缩小筛选条件的第一个查询仅用于检索该行的数据,第二个查询使用COUNTwithWHERE子句计算位置。

例如你的情况

查询1:

SELECT * FROM tbl WHERE name = 'Beta'

查询2:

SELECT COUNT(1) FROM tbl WHERE name >= 'Beta'

我们在具有2M记录的表中使用此方法,并且比OMG Ponies的方法更具可伸缩性。


4

其他答案对我来说似乎太复杂了。

这是一个简单的示例,假设您有一个包含列的表:

userid | points

并且您想要按点对用户标识进行排序并获得行位置(用户的“排名”),然后使用:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, userid, points
FROM
    ourtable
ORDER BY points DESC

num 给您行位置(排名)。

如果您具有MySQL 8.0+,则可能要使用ROW_NUMBER()


2

表中行的位置表示比目标行“好”多少行。

因此,您必须计算这些行。

tableWHERE name<'Beta'中选择COUNT(*)+ 1

如果出现平局,则返回最高位置。

如果您在现有的“测试版”行之后添加另一行“测试版”,则返回的位置仍为2,因为它们在分类中的位置相同。

希望这对将来会搜寻类似内容的人有所帮助,因为我相信问题所有者已经解决了他的问题。


2

我有一个非常非常相似的问题,这就是为什么我不会问相同的问题,但是我将在这里分享我的工作,我也必须使用分组和按AVG排序。有些学生带有签名和socore,我必须对它们进行排名(换句话说,我首先计算AVG,然后在DESC中对它们进行排序,最后我需要添加职位(对我来说排名),所以我做了一些非常相似最佳答案在这里,与调整我的问题),一个小的变化:

我终于将position(对我来说排名)列放在外部SELECT中

SET @rank=0;
SELECT @rank := @rank + 1 AS ranking, t.avg, t.name
  FROM(SELECT avg(students_signatures.score) as avg, students.name as name
FROM alumnos_materia
JOIN (SELECT @rownum := 0) r
left JOIN students ON students.id=students_signatures.id_student
GROUP BY students.name order by avg DESC) t 

这个答案比被接受的答案更容易理解。+1
埃里克·海斯特兰德'17

1

我正在接受可接受的答案,它似乎有点复杂,所以这里是它的简化版本。

SELECT t,COUNT(*) AS position FROM t      
 WHERE name <= 'search string' ORDER BY name

0

我有类似类型的问题,我需要table的rank(Indexorder by votes desc。以下对我来说很好用。

Select *, ROW_NUMBER() OVER(ORDER BY votes DESC) as "rank"
From "category_model"
where ("model_type" = ? and "category_id" = ?)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.