MySQL: obtenga el número de fila en select


181

¿Puedo ejecutar una instrucción select y obtener el número de fila si los elementos están ordenados?

Tengo una mesa 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    |                |
+-------------+---------------------+------+-----+---------+----------------+

Entonces puedo ejecutar esta consulta para obtener el número de pedidos por ID:

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

Esto me da un recuento de cada uno itemIDen la tabla de esta manera:

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

También quiero obtener el número de fila, por lo que podría decir que itemID=388es la primera fila, la 234segunda, etc. (esencialmente la clasificación de los pedidos, no solo un recuento sin procesar). Sé que puedo hacer esto en Java cuando recupere el resultado, pero me preguntaba si había una manera de manejarlo solo en SQL.

Actualizar

Establecer el rango lo agrega al conjunto de resultados, pero no se ordena correctamente:

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
Para referencia futura: si desea ordenar del rango 1 al rango 5, use ORDER BY rank ASC(ordenar por rango en orden ASCending). Supongo que eso es lo que quiere decir con , pero no ordena correctamente
BlueCacti

Respuestas:


180

Mira esto .

Cambie su consulta a:

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

La última selección es tu cuenta.


1
Eso agrega el rango al conjunto de resultados, pero no los pone en el orden correcto - pregunta actualizada con resultados
George

1
Intente mantener el ORDER BY ordercount DESC, y luego ajuste toda la consulta en otra SELECTque obtenga todo del primero, pero ordena por la columna de clasificación (0 en este caso).
Mike Cialowicz

1
¿Puedes mostrar un ejemplo de esto? ¿Cómo envolvería las selecciones?
George

9
Mira la respuesta de
swamibebop

1
@ MikeCialowicz, esto no funciona . Consulte mi solución o la solución de Swamibebop para obtener la respuesta correcta.
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
Gracias por aclarar, esto resolvió el problema fuera de servicio que estaba teniendo.
thaddeusmt

1
Gracias, esto fue realmente útil para mí :) Me sorprende que no haya una forma más directa de obtener 'índices' de fila de un conjunto de resultados ... pero de todos modos, gracias, esto fue útil.
rata

Puede agregar una cuarta fila con una cuenta total incremental cambiando la primera instrucción select en SELECT \ @rn: = \ @ rn + 1 AS rank, itemID, ordercount, \ @tot: = \ @ tot + ordercount como totalcount. Para definir el valor inicial de \ @tot, esto debe agregarse después de t2: (SELECCIONAR \ @tot: = 0) t3. Elimine el \ before before \ @, que tuve que usar para evitar el formato de mini-Markdown.
Jan Ehrhardt

2
¿Alguien puede explicar la relevancia de t1y t2?
Jared

2
@Jared, la sintaxis de MySQL solo necesita algo para estar allí. Puede ser cualquier cosa, incluso xy y.
Pacerier

31

La solución de Swamibebop funciona, pero al aprovechar la table.*sintaxis, podemos evitar repetir los nombres de columna del interno selecty obtener un resultado más simple / más corto:

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

Entonces eso te 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;

¿Sabe por casualidad por qué funciona el uso @r := @r + 1de una instrucción select, pero si está en un procedimiento almacenado con declare r int; set r = 0;, se queja (encendido r := r +1)?
Dan M.

@Pacerier, ¿también está garantizado el orden de las filas de la segunda selección en alguna parte? Sé que el orden de las filas devueltas por la selección sin orden por cláusula no está garantizado en ninguna parte, y la selección más externa es exactamente eso, aunque selecciona de la selección ordenada interna, por lo que podría ser una excepción. Sin embargo, si no es así, no puedo ver cómo se trata de una solución correcta, ya que tendrá el mismo defecto que el Mike de Chibu: no hay garantía en qué orden seleccionará los registros y los numerará.
Dan M.

¿Tendrías alguna idea de por qué ORDER BY no funciona cuando no está en la lista de campos? Vea mi resultado: hastebin.com/aluqefunoy.rb
Invierno

11

Puede usar variables MySQL para hacerlo. Algo como esto debería funcionar (sin embargo, consta de dos consultas).

SELECT 0 INTO @x;

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

2
Cuidado, esto no funcionaría porque order bysucede después de que la variable @xha sido evaluada. Intenta experimentar ordenando usando las otras columnas. También experimente con ambos descy asc. Verá que muchas veces fallarán y las únicas veces que funciona, es por pura suerte debido a que el orden de su "selección" original tiene el mismo orden que el orden de order by. Vea mi solución y / o la solución de Swamibebop.
Pacerier

@Pacerier, ¿estás seguro de eso? Me cansé de una consulta similar en un ejemplo diferente (básicamente seleccionar de la columna de números y numerarlos según su orden), parecía que si ordenaba por var / row num, cuando cambiaba el orden de las filas resultantes, pero cada número tenía el mismo número de fila. Pero si ordeno por la columna de números, entonces ASC/ DESCcambiaría el orden en que se numeraron esos números (de menor a mayor o viceversa). Parece que en ese caso order byse evaluó primero.
Dan M.

1

Ahora está integrado en MySQL 8.0 y 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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.