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, но кажется расточительным загружать потенциально большой набор результатов только для одной строки.



Ответы:


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, когда первый запрос даст позицию 2 для одного из них и 3 для другого ...


@actual: нечего сказать - нет альтернативы, кроме перехода к конкуренту, который поддерживает аналитические функции (PostgreSQL, Oracle, SQL Server, DB2 ...)
OMG Ponies

2
@OMGPonies Просто забудьте запятую после position, но это прекрасно.
Pierallard 02

Я знаю, что это довольно старый пост, но мне нужно подобное решение. Однако при использовании внутренних объединений, группировки и упорядочения по полю «позиция» они игнорируются, и все значения смешиваются. Какие-нибудь решения?
Дэниел

@ Дэниел, ты должен задать новый вопрос и, возможно, обратиться к этому.
PeerBr

@actual, поскольку это запрос для одной строки, здесь не должно быть серьезной проблемы с производительностью. Другое дело, если вы пытаетесь получить ранги из полного списка, но вы можете просто «обмануть» и использовать неявный ранг, упорядочивая по очкам.
AgmLauncher

20

Это единственный способ, о котором я могу думать:

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

2
+1 Хороший трюк ... Однако вы, вероятно, захотите использовать name <= 'Beta'вместо этого
Даниэль Вассалло

Такой подход даст те же positionзначения для галстуков.
OMG Ponies

(Удалил мой предыдущий комментарий - я был неправ) ... Что, если добавить LIMIT 1туда? В случае ничьей вы получите только один ряд с последней позицией галстука.
Даниэль Вассалло

Если OP может гарантировать, что это nameполе уникально, то нет причин усложнять запрос. Если не может - то подождем его ожидания результата на ничейные имена.
zerkms 01

8

Если запрос простой и размер возвращаемого набора результатов потенциально велик, вы можете попытаться разделить его на два запроса.

Первый запрос с ограниченными критериями фильтрации только для извлечения данных из этой строки, а второй запрос использует предложение COUNTwith WHEREдля вычисления позиции.

Например в вашем случае

Запрос 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

Положение строки в таблице показывает, сколько строк «лучше» целевой строки.

Итак, вы должны посчитать эти строки.

ВЫБЕРИТЕ СЧЕТЧИК (*) + 1 ОТ tableГДЕ name<'Бета'

В случае ничьей возвращается самая высокая позиция.

Если вы добавите еще одну строку с тем же именем «Бета» после существующей строки «Бета», то возвращенная позиция будет по-прежнему 2, поскольку они будут занимать одно и то же место в классификации.

Надеюсь, это поможет людям, которые будут искать что-то подобное в будущем, поскольку я считаю, что владелец вопроса уже решил свою проблему.


2

У меня очень похожая проблема, поэтому я не буду задавать тот же вопрос, но я расскажу здесь, что я сделал, мне пришлось использовать также группу по и заказ по AVG. Есть студенты с подписями и сокором, и мне пришлось их ранжировать (другими словами, я сначала вычисляю 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
Эрик Систранд

1

Я просматривал принятый ответ, и он казался немного сложным, поэтому вот его упрощенная версия.

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

0

У меня есть проблемы аналогичного типа, когда мне требуется ранг ( индекс ) таблицы order by votes desc. Следующее отлично работает для меня.

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

-9

может быть то, что вам нужно, это добавить синтаксис

LIMIT

так что используйте

SELECT * FROM tbl ORDER BY name ASC LIMIT 1

если вам нужна всего одна строка ..


этот ответ не решает проблему здесь. Вы можете подумать об его удалении
Дамиан Рафаэль Латтенеро
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.