Avec MySQL, comment puis-je générer une colonne contenant l'index d'enregistrement dans une table?

100

Est-il possible d'obtenir le numéro de ligne réel à partir d'une requête?

Je veux pouvoir commander une table appelée league_girl par un champ appelé score; et renvoyez le nom d'utilisateur et la position de ligne réelle de ce nom d'utilisateur.

Je veux classer les utilisateurs afin que je puisse dire où se trouve un utilisateur particulier, c'est-à-dire. Joe est en position 100 sur 200, c'est-à-dire

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

J'ai vu quelques solutions ici, mais j'ai essayé la plupart d'entre elles et aucune d'entre elles ne renvoie le numéro de ligne.

J'ai essayé ceci:

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

Comme dérivé

... mais il ne semble pas retourner la position de la ligne.

Des idées?

TheBounder
la source
la ligne est-elle un nom de fichier ou vous souhaitez classer par clé primaire?
Sarfraz
En SQL, les numéros de ligne ne sont vraiment pas importants. Ce que vous devez faire est d'ajouter une clé primaire à incrémentation automatique à votre table.
simendsjo
9
La clé primaire ne doit JAMAIS utiliser d'identifiant de ligne car ils ne sont pas fiables pour la position réelle de la ligne.
TheBounder
3
En outre, puisque le numéro de ligne serait une fonction du score qui, je suppose, n'est pas une valeur statique, en faire une valeur auto incrémentée (clé primaire ou non) ne donnerait pas le résultat attendu.
kasperjj
vous voudrez peut-être enregistrer le laid pour une fonction personnalisée, voir datamakessense.com/mysql-rownum-row-number-function
AdrianBR

Réponses:

174

Vous pouvez essayer ce qui suit:

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)pièce permet l'initialisation de la variable sans nécessiter de SETcommande séparée .

Cas de test:

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);

Requête de test:

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;

Résultat:

+----------+----------+-------+------------+
| 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
la source
19
Vous pouvez également initialiser @curRowen remplaçant l' JOINinstruction par une virgule, comme ceci:FROM league_girl l, (SELECT @curRow := 0) r
Mike
2
@Mike: C'est vrai. Et c'est probablement plus soigné aussi. Merci d'avoir partagé cette astuce!
Daniel Vassallo
2
@smhnaji MySQL exige que chaque "table dérivée" reçoive un nom. J'ai décidé de l'appeler "r" :) ... Cela a peu d'utilité dans ce cas, mais vous l'utiliseriez normalement pour référencer les attributs de la table dérivée, comme s'il s'agissait d'une vraie table.
Daniel Vassallo
20
Les gens doivent savoir que ce numéro de ligne est calculé avant toute commande, de sorte que les numéros peuvent devenir confus si l'ordre change l'ordre des lignes.
Grim ...
7
Existe-t-il un moyen d'obtenir ce numéro de ligne calculé après le ORDER BY?
Pierre de LESPINAY
38
SELECT @i:=@i+1 AS iterator, t.*
FROM tablename t,(SELECT @i:=0) foo
Peter Johnson
la source
y a-t-il un moyen de le faire pour que la colonne de l'itérateur soit un entier et non une décimale?
kraftydevil
7

Voici la structure du modèle que j'ai utilisé:

  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

Et voici mon code de travail:

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
aiguilleur
la source
parfait, fonctionne comme le charme et le modèle peut être réutilisé avec la sous-requête comme paramètre ..
Zavael
Cette solution fonctionne également si votre requête de base utilise GROUP BY
Dave R
4

Vous pouvez aussi utiliser

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

pour initialiser la variable de compteur.

Foyer
la source
3
@curRowpeut encore avoir une valeur de la dernière fois que vous avez exécuté cette requête dans la session en cours.
Bill Karwin
Vrai, mais uniquement si vous effectuez une nouvelle requête sur la même instance de connexion. Les variables locales sont automatiquement supprimées une fois la connexion fermée.
Hearth
Oui, c'est ce que je voulais dire quand j'ai dit «dans la session en cours».
Bill Karwin
3

En supposant que MySQL le prend en charge, vous pouvez facilement le faire avec une sous-requête SQL standard:

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;

Pour de grandes quantités de résultats affichés, ce sera un peu lent et vous voudrez passer à une auto-jointure à la place.

ftzdomino
la source
3

Si vous souhaitez simplement connaître la position d'un utilisateur spécifique après classement par score de champ, vous pouvez simplement sélectionner toutes les lignes de votre tableau où le score de champ est supérieur au score de l'utilisateur actuel. Et utilisez le numéro de ligne retourné + 1 pour savoir quelle position de cet utilisateur actuel.

En supposant que votre table est league_girlet que votre champ principal est id, vous pouvez utiliser ceci:

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

J'ai trouvé la réponse originale incroyablement utile, mais je voulais également saisir un certain ensemble de lignes en fonction des numéros de ligne que j'insérais. En tant que tel, j'ai enveloppé toute la réponse d'origine dans une sous-requête afin de pouvoir référencer le numéro de ligne que j'insérais.

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;

Avoir une sous-requête dans une sous-requête n'est pas très efficace, il vaudrait donc la peine de tester si vous obtenez un meilleur résultat en demandant à votre serveur SQL de gérer cette requête, ou en récupérant la table entière et en demandant à l'application / serveur Web de manipuler les lignes après coup .

Personnellement, mon serveur SQL n'est pas trop occupé, il était donc préférable de le faire gérer les sous-requêtes imbriquées.

BasicExp
la source
0

Je sais que l'OP demande une mysqlréponse, mais comme j'ai trouvé les autres réponses ne fonctionnant pas pour moi,

  • La plupart d'entre eux échouent avec order by
  • Ou ils sont tout simplement très inefficaces et rendent votre requête très lente pour une table grasse

Alors pour gagner du temps pour d'autres comme moi, indexez simplement la ligne après les avoir récupérées de la base de données

exemple en PHP:

$users = UserRepository::loadAllUsersAndSortByScore();

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

exemple en PHP utilisant offset et limit pour la pagination:

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

$users = UserRepository::loadAllUsersAndSortByScore();

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