MySQL - Obter número da linha ao selecionar

181

Posso executar uma instrução select e obter o número da linha se os itens estiverem classificados?

Eu tenho uma tabela como esta:

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

Em seguida, posso executar esta consulta para obter o número de pedidos por ID:

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

Isso me dá uma contagem de cada um itemIDna tabela assim:

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

Também quero obter o número da linha, para poder dizer que itemID=388é a primeira linha, a 234segunda, etc. (essencialmente a classificação dos pedidos, não apenas uma contagem bruta). Eu sei que posso fazer isso em Java quando recupero o resultado, mas fiquei pensando se havia uma maneira de lidar com isso puramente no SQL.

Atualizar

A definição da classificação o adiciona ao conjunto de resultados, mas não está devidamente ordenado:

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)
George
fonte
1
Para referência futura: se você deseja ordenar da classificação 1 à classificação 5, use ORDER BY rank ASC(ordenação por classificação na ordem ASCending). Eu acho que é isso que você quer dizer com , mas não devidamente ordenado
BlueCacti
Possível duplicata de ROW_NUMBER () no MySQL
Ciro Santilli,

Respostas:

180

Dê uma olhada nisso .

Altere sua consulta para:

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

A última seleção é a sua contagem.

Mike Cialowicz
fonte
1
Isso adiciona a classificação ao conjunto de resultados, mas não colocá-los no bom fim - pergunta atualizado com os resultados
George
1
Tente manter o ORDER BY ordercount DESCe, em seguida, agrupe toda a consulta em outra SELECTque obtenha tudo da primeira, mas ordene pela coluna de classificação (0 neste caso).
Mike Cialowicz 26/03/10
1
Você pode mostrar um exemplo disso? Como envolveria as seleções?
George
9
Confira a resposta de swamibebop
thaddeusmt
1
@ MikeCialowicz, isso não funciona . Consulte a minha solução ou a solução da Swamibebop para obter a resposta certa.
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;
swamibebop
fonte
1
Obrigado por esclarecer, isso resolveu o problema fora de ordem que eu estava tendo.
Thaddeusmt 29/03
1
Obrigado, isso foi realmente útil para mim :) Estou surpreso por não haver uma maneira mais direta de obter 'índices' de linha de um conjunto de resultados ... mas, de qualquer forma, graças a isso foi útil.
rat
Você pode adicionar uma quarta linha com uma contagem total incremental alterando a primeira instrução de seleção em SELECT \ @rn: = \ @ rn + 1 classificação AS, itemID, contagem de pedidos, \ @tot: = \ @ tot + contagem de pedidos como contagem total. Para definir o valor inicial de \ @tot, este deve ser adicionado após o t2: (SELECT \ @tot: = 0) t3. Exclua o \ before every \ @, que eu tive que usar para contornar a formatação de min Markdown.
Jan Ehrhardt
2
Alguém pode explicar a relevância de t1e t2?
Jared
2
@ Jared, a sintaxe MySQL só precisa de algo para estar lá. Pode ser qualquer coisa, mesmo xe y.
Pacerier 24/04
31

A solução do Swamibebop funciona, mas, aproveitando a table.*sintaxe, podemos evitar a repetição dos nomes das colunas internas selecte obter um resultado mais simples / mais curto:

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

Então, isso lhe dará:

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;
Pacerier
fonte
Por acaso, você sabe por que o uso @r := @r + 1de uma instrução select funciona, mas se estiver em um procedimento armazenado com declare r int; set r = 0;, ele reclama (ativado r := r +1)?
Dan M.
@ Pacerier, também é garantida a ordem das linhas que o segundo selecione é garantido em algum lugar? Eu sei que a ordem das linhas retornadas pela seleção sem cláusula order by não é garantida em nenhum lugar, e a seleção mais externa é exatamente isso, embora selecione da seleção ordenada interna, portanto, pode ser uma exceção. Se não for, no entanto, não vejo como essa é uma solução correta, pois terá a mesma falha do Mike de Chibu - não há garantia de que a seleção de pedidos passará pelos registros e os numerará.
Dan M.
Você tem alguma idéia de por que o ORDER BY não está funcionando sempre que não está na lista de campos? Veja o meu resultado: rushbin.com/aluqefunoy.rb
Winter
11

Você pode usar variáveis ​​do MySQL para fazer isso. Algo assim deve funcionar (porém, consiste em duas consultas).

SELECT 0 INTO @x;

SELECT itemID, 
       COUNT(*) AS ordercount, 
       (@x:=@x+1) AS rownumber 
FROM orders 
GROUP BY itemID 
ORDER BY ordercount DESC; 
Chibu
fonte
2
Cuidado, isso não funcionaria porque order byacontece depois que a variável @xfoi avaliada. Tente experimentar solicitando usando as outras colunas. Também experimente com ambos desce asc. Você verá que muitas vezes eles falham e as únicas vezes em que funciona, é por pura sorte devido à ordem do seu "select" original ter a mesma ordem que a ordem de order by. Veja minha solução e / ou a solução Swamibebop.
Pacerier
@Pacerier você tem certeza disso? Eu cansei uma consulta semelhante em um exemplo diferente (basicamente selecione a partir da coluna de números e os numere de acordo com a ordem deles). Parece que, se eu pedi por var / row num, quando ele mudou a ordem das linhas resultantes, mas cada número tinha o mesmo número de linha. Mas se eu pedir pela coluna numérica, o ASC/ DESCmudará a ordem na qual esses números foram numerados (do menor para o maior ou vice-versa). Portanto, parece que nesse caso order byfoi avaliado primeiro.
Dan M.
1

Agora está embutido no MySQL 8.0 e 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
caram
fonte