Con MySQL, ¿cómo puedo generar una columna que contenga el índice de registro en una tabla?

100

¿Hay alguna forma de obtener el número de fila real de una consulta?

Quiero poder ordenar una tabla llamada league_girl por un campo llamado score; y devuelva el nombre de usuario y la posición real de la fila de ese nombre de usuario.

Quiero clasificar a los usuarios para poder saber dónde está un usuario en particular, es decir. Joe ocupa la posición 100 de 200, es decir

User Score Row
Joe  100    1
Bob  50     2
Bill 10     3

He visto algunas soluciones aquí, pero he probado la mayoría y ninguna de ellas devuelve el número de fila.

He intentado esto:

SELECT position, username, score
FROM (SELECT @row := @row + 1 AS position, username, score 
       FROM league_girl GROUP BY username ORDER BY score DESC) 

Como derivado

... pero no parece devolver la posición de la fila.

¿Algunas ideas?

TheBounder
fuente
¿La fila es un nombre archivado o desea ordenar por clave primaria?
Sarfraz
En SQL, los números de fila no son realmente importantes. Lo que debe hacer es agregar una clave primaria de incremento automático a su tabla.
simendsjo
9
La clave principal NUNCA debe ser un identificador de fila, ya que no es confiable para la posición real de la fila.
TheBounder
3
Además, dado que el número de fila sería una función de la puntuación que supongo que no es un valor estático, convertirlo en un valor incrementado automáticamente (clave primaria o no) no daría el resultado deseado.
kasperjj
es posible que desee guardar lo feo para una función personalizada, consulte datamakessense.com/mysql-rownum-row-number-function
AdrianBR

Respuestas:

174

Es posible que desee probar lo siguiente:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r;

La JOIN (SELECT @curRow := 0)parte permite la inicialización de la variable sin requerir un SETcomando por separado .

Caso de prueba:

CREATE TABLE league_girl (position int, username varchar(10), score int);
INSERT INTO league_girl VALUES (1, 'a', 10);
INSERT INTO league_girl VALUES (2, 'b', 25);
INSERT INTO league_girl VALUES (3, 'c', 75);
INSERT INTO league_girl VALUES (4, 'd', 25);
INSERT INTO league_girl VALUES (5, 'e', 55);
INSERT INTO league_girl VALUES (6, 'f', 80);
INSERT INTO league_girl VALUES (7, 'g', 15);

Consulta de prueba:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r
WHERE   l.score > 50;

Resultado:

+----------+----------+-------+------------+
| position | username | score | row_number |
+----------+----------+-------+------------+
|        3 | c        |    75 |          1 |
|        5 | e        |    55 |          2 |
|        6 | f        |    80 |          3 |
+----------+----------+-------+------------+
3 rows in set (0.00 sec)
Daniel Vassallo
fuente
19
También puede inicializar @curRowreemplazando la JOINdeclaración con una coma, así:FROM league_girl l, (SELECT @curRow := 0) r
Mike
2
@ Mike: Eso es cierto. Y eso probablemente también sea más ordenado. Gracias por compartir este consejo.
Daniel Vassallo
2
@smhnaji MySQL requiere que cada "tabla derivada" reciba un nombre. Decidí llamarlo "r" :) ... Tiene poco propósito en este caso, pero normalmente lo usarías para hacer referencia a atributos de la tabla derivada, como si fuera una tabla real.
Daniel Vassallo
20
Las personas deben tener en cuenta que este número de fila se calcula antes de realizar cualquier pedido, por lo que los números pueden mezclarse si el orden cambia el orden de las filas.
Grim ...
7
¿Hay alguna manera de calcular este número de fila después de ORDER BY?
Pierre de LESPINAY
38
SELECT @i:=@i+1 AS iterator, t.*
FROM tablename t,(SELECT @i:=0) foo
Peter Johnson
fuente
¿Hay alguna manera de hacer esto para que la columna del iterador sea un número entero y no un decimal?
kraftydevil
7

Aquí viene la estructura de la plantilla que utilicé:

  select
          /*this is a row number counter*/
          ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
          as rownumber,
          d3.*
  from 
  ( select d1.* from table_name d1 ) d3

Y aquí está mi código de trabajo:

select     
           ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
           as rownumber,
           d3.*
from
(   select     year( d1.date ), month( d1.date ), count( d1.id )
    from       maindatabase d1
    where      ( ( d1.date >= '2013-01-01' ) and ( d1.date <= '2014-12-31' ) )
    group by   YEAR( d1.date ), MONTH( d1.date ) ) d3
goadreamer
fuente
perfecto, funciona como encanto y plantilla se puede reutilizar con subconsulta como parámetro ..
Zavael
Esta solución también funciona si su consulta base usa GROUP BY
Dave R
4

También puedes usar

SELECT @curRow := ifnull(@curRow,0) + 1 Row, ...

para inicializar la variable del contador.

Hogar
fuente
3
@curRowaún puede tener un valor de la última vez que ejecutó esta consulta en la sesión actual.
Bill Karwin
Verdadero, pero solo si vuelve a consultar en la misma instancia de conexión. Las variables locales se eliminan automáticamente una vez que se cierra la conexión.
Hearth
Sí, eso es lo que quise decir cuando dije "en la sesión actual".
Bill Karwin
3

Suponiendo que MySQL lo admita, puede hacer esto fácilmente con una subconsulta estándar de SQL:

select 
    (count(*) from league_girl l1 where l2.score > l1.score and l1.id <> l2.id) as position,
    username,
    score
from league_girl l2
order by score;

Para grandes cantidades de resultados mostrados, esto será un poco lento y, en su lugar, querrá cambiar a una autounión.

ftzdomino
fuente
3

Si solo desea saber la posición de un usuario específico después de ordenar por puntaje de campo, simplemente puede seleccionar todas las filas de su tabla donde el puntaje de campo es más alto que el puntaje del usuario actual. Y use el número de fila devuelto + 1 para saber qué posición de este usuario actual.

Suponiendo que su tabla es league_girly su campo principal lo es id, puede usar esto:

SELECT count(id) + 1 as rank from league_girl where score > <your_user_score>
Heryno
fuente
0

Encontré la respuesta original increíblemente útil, pero también quería tomar un cierto conjunto de filas en función de los números de fila que estaba insertando. Como tal, envolví toda la respuesta original en una subconsulta para poder hacer referencia al número de fila que estaba insertando.

SELECT * FROM 
( 
    SELECT *, @curRow := @curRow + 1 AS "row_number"
    FROM db.tableName, (SELECT @curRow := 0) r
) as temp
WHERE temp.row_number BETWEEN 1 and 10;

Tener una subconsulta en una subconsulta no es muy eficiente, por lo que valdría la pena probar si obtiene un mejor resultado haciendo que su servidor SQL maneje esta consulta, o obteniendo la tabla completa y haciendo que la aplicación / servidor web manipule las filas después del hecho. .

Personalmente, mi servidor SQL no está demasiado ocupado, por lo que era preferible que manejara las subconsultas anidadas.

BasicExp
fuente
0

Sé que el OP está pidiendo una mysqlrespuesta, pero como encontré que las otras respuestas no me funcionan,

  • La mayoría de ellos fracasan con order by
  • O simplemente son muy ineficientes y hacen que su consulta sea muy lenta para una tabla gruesa

Entonces, para ahorrar tiempo a otros como yo, simplemente indexe la fila después de recuperarlos de la base de datos

ejemplo en PHP:

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1;
}

ejemplo en PHP usando desplazamiento y límite para paginación:

$limit = 20; //page size
$offset = 3; //page number

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1+($limit*($offset-1));
}
azerafati
fuente