MySQL - получить номер строки при выборе


181

Могу ли я выполнить оператор выбора и получить номер строки, если элементы отсортированы?

У меня есть такая таблица:

mysql> describe orders;
+-------------+---------------------+------+-----+---------+----------------+
| Field       | Type                | Null | Key | Default | Extra          |
+-------------+---------------------+------+-----+---------+----------------+
| orderID     | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| itemID      | bigint(20) unsigned | NO   |     | NULL    |                |
+-------------+---------------------+------+-----+---------+----------------+

Затем я могу выполнить этот запрос, чтобы получить количество заказов по идентификатору:

SELECT itemID, COUNT(*) as ordercount
FROM orders
GROUP BY itemID ORDER BY ordercount DESC;

Это дает мне счет каждого itemIDв таблице следующим образом:

+--------+------------+
| itemID | ordercount |
+--------+------------+
|    388 |          3 |
|    234 |          2 |
|   3432 |          1 |
|    693 |          1 |
|   3459 |          1 |
+--------+------------+

Я также хочу получить номер строки, чтобы я мог сказать, что itemID=388это первая строка, 234вторая и т. Д. (По сути, это ранжирование ордеров, а не просто подсчет). Я знаю, что могу сделать это в Java, когда получу результат обратно, но мне было интересно, есть ли способ справиться с этим чисто в SQL.

Обновить

Установка ранга добавляет его к результирующему набору, но не в правильном порядке:

mysql> SET @rank=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @rank:=@rank+1 AS rank, itemID, COUNT(*) as ordercount
    -> FROM orders
    -> GROUP BY itemID ORDER BY rank DESC;
+------+--------+------------+
| rank | itemID | ordercount |
+------+--------+------------+
|    5 |   3459 |          1 |
|    4 |    234 |          2 |
|    3 |    693 |          1 |
|    2 |   3432 |          1 |
|    1 |    388 |          3 |
+------+--------+------------+
5 rows in set (0.00 sec)

1
Для дальнейшего использования: если вы хотите сделать заказ от ранга 1 до ранга 5, используйте ORDER BY rank ASC(упорядочение по рангу в порядке ASCending). Я думаю, это то, что вы имеете в виду, но не правильно упорядочены
BlueCacti

Ответы:


180

Посмотрите на это .

Измените ваш запрос на:

SET @rank=0;
SELECT @rank:=@rank+1 AS rank, itemID, COUNT(*) as ordercount
  FROM orders
  GROUP BY itemID
  ORDER BY ordercount DESC;
SELECT @rank;

Последний выбор - ваш счет.


1
Это добавляет рейтинг к результирующему набору, но не ставит их в правильном порядке - обновленный вопрос с результатами
Джордж

1
Попробуйте сохранить ORDER BY ordercount DESC, а затем обернуть весь запрос в другой, SELECTкоторый получает все от первого, но упорядочивает по столбцу ранга (в данном случае 0).
Майк Цалович

1
Можете ли вы показать пример этого? Как бы я обернуть выбор?
Джордж

9
Проверьте ответ
Свамибопа

1
@MikeCialowicz, это не работает . Обратитесь к моему решению или решению Swamibebop для правильного ответа.
Pacerier

178
SELECT @rn:=@rn+1 AS rank, itemID, ordercount
FROM (
  SELECT itemID, COUNT(*) AS ordercount
  FROM orders
  GROUP BY itemID
  ORDER BY ordercount DESC
) t1, (SELECT @rn:=0) t2;

1
Спасибо за разъяснение, это решило проблему не по порядку, которая у меня была.
thaddeusmt

1
Спасибо, это было действительно полезно для меня :) Я удивлен, что нет более простого способа получить «индексы» строк из набора результатов ... но в любом случае, спасибо, это было удобно.
крыса

Вы можете добавить четвертую строку с добавочным итоговым счетом, изменив первый оператор выбора в SELECT \ @rn: = \ @ rn + 1 AS rank, itemID, ordercount, \ @tot: = \ @ tot + ordercount как totalcount. Чтобы определить начальное значение \ @tot, его следует добавить после t2: (SELECT \ @tot: = 0) t3. Удалите \ перед каждым \ @, который мне пришлось использовать, чтобы обойти мини-форматирование Markdown.
Ян Эрхардт

2
Может кто-нибудь объяснить актуальность t1и t2?
Джаред

2
@Jared, синтаксис MySQL просто должен что-то там быть. Это может быть что угодно, даже xи y.
Pacerier

31

Решение Swamibebop работает, но, используя преимущества table.*синтаксиса, мы можем избежать повторения имен столбцов внутреннего selectи получить более простой / короткий результат:

SELECT @r := @r+1 , 
       z.* 
FROM(/* your original select statement goes in here */)z, 
(SELECT @r:=0)y;

Так что это даст вам:

SELECT @r := @r+1 , 
       z.* 
FROM(
     SELECT itemID, 
     count(*) AS ordercount
     FROM orders
     GROUP BY itemID
     ORDER BY ordercount DESC
    )z,
    (SELECT @r:=0)y;

Вы случайно не знаете, почему использование @r := @r + 1в операторе select работает, но если оно в хранимой процедуре с declare r int; set r = 0;, оно жалуется (на r := r +1)?
Дэн М.

@Pacerier, также гарантирован ли порядок строк при втором выборе? Я знаю, что порядок строк, возвращаемых оператором select без заказа order by, нигде не гарантирован, и самый внешний выбор именно такой, хотя он выбирается из внутреннего упорядоченного выбора, поэтому он может быть исключением. Однако, если это не так, я не могу понять, насколько это правильное решение, поскольку у него будет тот же недостаток, что и у Майка Чибу - нет гарантии, в каком порядке выбора будут проходить записи и нумероваться.
Дэн М.

Не могли бы вы понять, почему ORDER BY не работает, если его нет в списке полей? Смотрите мой результат: hastebin.com/aluqefunoy.rb
зима

11

Вы можете использовать переменные MySQL, чтобы сделать это. Нечто подобное должно работать (хотя оно состоит из двух запросов).

SELECT 0 INTO @x;

SELECT itemID, 
       COUNT(*) AS ordercount, 
       (@x:=@x+1) AS rownumber 
FROM orders 
GROUP BY itemID 
ORDER BY ordercount DESC; 

2
Осторожно, это не сработает, потому что order byпроисходит после того, как переменная @xбыла оценена. Попробуйте поэкспериментировать, заказав другие столбцы. Также экспериментируйте с обоими descи asc. Вы увидите, что много раз они терпят неудачу, и единственные времена, когда это работает, это по счастливой случайности из-за того, что порядок вашего исходного «выбора» имеет тот же порядок, что и порядок order by. Смотрите мое решение и / или решение Swamibebop.
Pacerier

@Pacerier ты уверен в этом? Я устал с подобным запросом в другом примере (в основном выберите из столбца чисел, и нумеруйте их в соответствии с их порядком), казалось, что если я упорядочил по var / row num, когда он изменил порядок результирующих строк, но у каждого числа была одна и та же строка num. Но если бы я упорядочил по столбцу чисел, то ASC/ DESCизменил бы порядок, в котором эти числа были пронумерованы (от наименьшего к наибольшему или наоборот). Так что, похоже, что в этом случае order byбыл оценен в первую очередь.
Дэн М.

1

Теперь он встроен в MySQL 8.0 и MariaDB 10.2:

SELECT
  itemID, COUNT(*) as ordercount,
  ROW_NUMBER OVER (PARTITION BY itemID ORDER BY rank DESC) as rank
FROM orders
GROUP BY itemID ORDER BY rank DESC
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.