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

181

Puis-je exécuter une instruction de sélection 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 chacun itemIDdans le tableau comme ceci:

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

Je veux aussi obtenir le numéro de ligne, donc je pourrais dire que itemID=388c'est la première ligne, la 234deuxième, etc. (essentiellement le classement des commandes, pas seulement un décompte brut). Je sais que je peux faire cela en Java lorsque je récupère le résultat, mais je me demandais s'il y avait un moyen de le gérer uniquement en SQL.

Mettre à jour

La définition du rang l'ajoute à l'ensemble de résultats, mais pas correctement ordonné:

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
la source
1
Pour référence future: Si vous souhaitez passer du rang 1 au rang 5, utilisez ORDER BY rank ASC(classement par rang dans l'ordre ASCending). Je suppose que c'est ce que vous entendez par mais pas correctement commandé
BlueCacti
Possible duplication de ROW_NUMBER () dans MySQL
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

180

Jetez un œil à ceci .

Modifiez 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;
SELECT @rank;

Le dernier choix est votre décompte.

Mike Cialowicz
la source
1
Cela ajoute le rang à l'ensemble de résultats, mais ne les met pas dans le bon ordre - question mise à jour avec résultats
George
1
Essayez de conserver le ORDER BY ordercount DESC, puis enveloppez toute la requête dans une autre SELECTqui récupère tout à partir de la première, mais triée par la colonne de rang (0 dans ce cas).
Mike Cialowicz
1
Pouvez-vous en montrer un exemple? Comment emballer les sélections?
George
9
Découvrez la réponse de swamibebop
thaddeusmt
1
@MikeCialowicz, cela ne fonctionne pas . Référez-vous à ma solution ou à la solution Swamibebop pour la bonne réponse.
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
la source
1
Merci d'avoir clarifié, cela a résolu le problème de panne que j'avais.
thaddeusmt
1
Merci, cela a été vraiment utile pour moi :) Je suis surpris qu'il n'y ait pas de moyen plus simple d'obtenir des «index» de ligne à partir d'un jeu de résultats ... mais de toute façon merci c'était pratique.
rat
Vous pouvez ajouter une quatrième ligne avec un totalcount incrémentiel en modifiant la première instruction de sélection dans SELECT \ @rn: = \ @ rn + 1 AS rank, itemID, ordercount, \ @tot: = \ @ tot + ordercount comme totalcount. Pour définir la valeur initiale de \ @tot, cela doit être ajouté après le t2: (SELECT \ @tot: = 0) t3. Supprimez le \ avant chaque \ @, que j'ai dû utiliser pour contourner le formatage mini-Markdown.
Jan Ehrhardt
2
Quelqu'un peut-il expliquer la pertinence de t1et t2?
Jared
2
@Jared, la syntaxe MySQL a juste besoin de quelque chose pour être là. Cela peut être n'importe quoi, même xet y.
Pacerier
31

La solution de Swamibebop fonctionne, mais en tirant parti de la table.*syntaxe, nous pouvons éviter de répéter les noms de colonne de l'intérieur selectet 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;

Cela vous donnera donc:

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
la source
Savez-vous par hasard pourquoi utiliser @r := @r + 1dans une instruction select fonctionne, mais si c'est dans une procédure stockée avec declare r int; set r = 0;, il se plaint (on r := r +1)?
Dan M.
@Pacerier, l'ordre des lignes de la deuxième sélection est-il également garanti quelque part? Je sais que l'ordre des lignes renvoyées par le select sans clause order by n'est garanti nulle part, et le select le plus externe est exactement cela, bien qu'il sélectionne dans le select ordonné interne, donc cela pourrait être une exception. Si ce n'est pas le cas, cependant, je ne vois pas en quoi cette solution est correcte car elle aura le même défaut que celui de Mike de Chibu - aucune garantie dans quel ordre sélectionner passera par les enregistrements et les numérotera.
Dan M.
Auriez-vous une idée de la raison pour laquelle ORDER BY ne fonctionne pas lorsqu'il n'est pas dans la liste des champs? Voir mon résultat: hastebin.com/aluqefunoy.rb
Hiver
11

Vous pouvez utiliser des variables MySQL pour le faire. Quelque chose comme ça devrait fonctionner (cependant, il se compose de 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; 
Chibu
la source
2
Attention, cela ne fonctionnerait pas car order bycela se produit après que la variable @xa été évaluée. Essayez de faire des essais en classant en utilisant les autres colonnes. Expérimentez également avec descet asc. Vous verrez que plusieurs fois ils échoueront et que les seuls moments où cela fonctionne, c'est par pure chance en raison de l'ordre de votre "select" d'origine ayant le même ordre que celui de order by. Voir ma solution et / ou la solution de Swamibebop.
Pacerier
@Pacerier en êtes-vous sûr? J'ai fatigué une requête similaire dans un exemple différent (en gros, sélectionnez dans la colonne de nombres et numérotez-les en fonction de leur ordre), il semblait que si je commandais par var / row num, quand cela changeait l'ordre des lignes résultantes, mais chaque numéro avait le même numéro de ligne. Mais si je commande par la colonne de nombre, alors le ASC/ DESCchangerait l'ordre dans lequel ces nombres ont été numérotés (du plus petit au plus grand ou vice versa). Donc, il semble que dans ce cas, a order byété évalué en premier.
Dan M.
1

Il est maintenant intégré à 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
caram
la source