205 votes

MySql - Obtenir le numéro de ligne lors de la sélection

Puis-je exécuter une instruction select et obtenir le numéro de ligne si les éléments sont triés ?

J'ai une table comme celle-ci :

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

Je peux ensuite exécuter cette requête pour obtenir le nombre de commandes par ID :

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

Cela me donne un compte de chaque itemID dans la table comme ceci :

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

Je veux également obtenir le numéro de ligne, afin de pouvoir dire que l'itemID 388 est la première ligne, 234 la deuxième, etc. (essentiellement le classement des commandes, et pas seulement un compte brut). Je sais que je peux faire cela en Java lorsque je reçois l'ensemble des résultats, mais je me demandais s'il y avait un moyen de le faire purement en SQL.

Mise à jour

La définition du rang l'ajoute à l'ensemble des résultats, mais sans l'ordonner correctement :

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 votes

Pour référence future : Si vous voulez commander du rang 1 au rang 5, utilisez ORDER BY rank ASC (classement par rang dans l'ordre croissant de l'ASC). Je suppose que c'est ce que vous voulez dire par mais n'a pas été correctement commandé

0 votes

Duplicata possible de ROW_NUMBER() dans MySQL

200voto

Mike Cialowicz Points 4490

Jetez un coup d'œil à ce .

Changez votre requête en :

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

1 votes

Cela ajoute le rang à l'ensemble des résultats, mais ne les place pas dans le bon ordre - question mise à jour avec les résultats.

2 votes

Essayez de garder le ORDER BY ordercount DESC et ensuite envelopper la requête entière dans une autre SELECT qui récupère tout du premier, mais ordonne par la colonne de rang (0 dans ce cas).

2 votes

Pouvez-vous montrer un exemple ? Comment emballer les sélections ?

189voto

swamibebop Points 729
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 votes

Merci pour cette clarification, cela a résolu le problème de désordre que j'avais.

1 votes

Merci, c'était vraiment utile pour moi :) Je suis surpris qu'il n'y ait pas un moyen plus direct d'obtenir des "index" de lignes à partir d'un ensemble de résultats... mais en tout cas, merci, c'était pratique.

0 votes

Vous pouvez ajouter une quatrième ligne avec un compte total incrémentiel en modifiant la première instruction de sélection dans SELECT \@rn:=\@rn+1 AS rank, itemID, ordercount, \@tot:=\@tot+ordercount as totalcount. Pour définir la valeur initiale de \@tot, il faut l'ajouter après le t2 : (SELECT \@tot:=0) t3. Supprimez le \ avant chaque \@, que j'ai dû utiliser pour contourner le formatage mini-Markdown.

39voto

Pacerier Points 15960

La solution de Swamibebop fonctionne, mais en tirant parti de *`table.** syntaxe, nous pouvons [éviter de répéter](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) les noms des colonnes de l'intérieurselect` et obtenir un résultat plus simple/plus court :

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

Donc ça vous donnera :

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;

0 votes

Savez-vous par hasard pourquoi utiliser @r := @r + 1 dans une instruction de sélection fonctionne, mais si elle se trouve dans une procédure stockée avec declare r int; set r = 0; il se plaint (sur r := r +1 )?

0 votes

@Pacerier, est-ce que l'ordre des lignes de la deuxième sélection est garanti quelque part ? Je sais que l'ordre des lignes retournées par le select sans clause order by n'est garanti nulle part, et le select le plus extérieur est exactement cela, bien qu'il sélectionne à partir du select ordonné intérieur, donc il pourrait être une exception. Si ce n'est pas le cas, cependant, je ne vois pas en quoi cette solution est correcte puisqu'elle aura le même défaut que celle de Mike de Chibu - aucune garantie quant à l'ordre dans lequel la sélection va parcourir les enregistrements et les numéroter.

0 votes

Auriez-vous une idée de la raison pour laquelle le ORDER BY ne fonctionne pas lorsqu'il ne figure pas dans la liste des champs ? Voir mon résultat : hastebin.com/aluqefunoy.rb

13voto

Chibu Points 1219

Vous pouvez utiliser une variable mysql pour le faire. Quelque chose comme ceci devrait fonctionner (bien que ce soit deux requêtes).

SELECT 0 INTO @x;

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

2 votes

Attention, ça ne marcherait pas parce que order by se produit après la variable @x a été évalué. Essayez d'expérimenter en ordonnant en utilisant les autres colonnes. Expérimentez également avec les deux desc y asc . Vous verrez qu'ils échouent souvent et que les seules fois où ça marche, c'est en la chance pure parce que l'ordre de votre "select" original est le même que l'ordre des "select". order by . Voir ma solution et/ou la solution de Swamibebop.

0 votes

@Pacerier êtes-vous sûr de cela ? J'ai utilisé une requête similaire dans un autre exemple (en fait, je sélectionne dans la colonne des nombres, et je les numérote selon leur ordre). Il semble que si j'ordonne par var/row num, l'ordre des lignes résultantes est modifié, mais chaque nombre a le même numéro de ligne. Mais si j'ordonne par la colonne des nombres, alors l'ordre des lignes résultantes est modifié. ASC / DESC changerait l'ordre dans lequel ces numéros sont numérotés (du plus petit au plus grand ou vice versa). Il semble donc que dans ce cas order by a été évalué en premier.

11voto

caram Points 854

Il est maintenant intégré dans MySQL 8.0 et 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X