web-dev-qa-db-fra.com

MySQL obtient la position de la ligne dans ORDER BY

Avec la table MySQL suivante:

+-----------------------------+
+ id INT UNSIGNED             +
+ name VARCHAR(100)           +
+-----------------------------+

Comment puis-je sélectionner une nique rangée ET sa position parmi les autres rangées du tableau, lorsqu'elles sont triées par name ASC. Donc, si les données de la table ressemblent à ceci, lorsqu'elles sont triées par nom:

+-----------------------------+
+ id | name                   +
+-----------------------------+
+  5 | Alpha                  +
+  7 | Beta                   +
+  3 | Delta                  +
+ .....                       +
+  1 | Zed                    +
+-----------------------------+

Comment pourrais-je sélectionner la rangée Beta pour obtenir la position actuelle de cette rangée? Le résultat que je recherche devrait ressembler à ceci:

+-----------------------------+
+ id | position | name        +
+-----------------------------+
+  7 |        2 | Beta        +
+-----------------------------+

Je peux faire un simple SELECT * FROM tbl ORDER BY name ASC énumère ensuite les lignes en PHP, mais il semble inutile de charger un ensemble de résultats potentiellement volumineux pour une seule ligne.

77
leepowers

Utilisez ceci:

SELECT x.id, 
       x.position,
       x.name
  FROM (SELECT t.id,
               t.name,
               @rownum := @rownum + 1 AS position
          FROM TABLE t
          JOIN (SELECT @rownum := 0) r
      ORDER BY t.name) x
 WHERE x.name = 'Beta'

... pour obtenir une valeur de position unique. Cette:

SELECT t.id,
       (SELECT COUNT(*)
          FROM TABLE x
         WHERE x.name <= t.name) AS position,
       t.name    
  FROM TABLE t      
 WHERE t.name = 'Beta'

... donnera la même valeur aux liens. IE: S'il y a deux valeurs à la deuxième place, elles auront toutes deux une position de 2 lorsque la première requête donnera une position de 2 à l'une d'elles et de 3 à l'autre ...

108
OMG Ponies

C’est le seul moyen auquel je puisse penser:

SELECT `id`,
       (SELECT COUNT(*) FROM `table` WHERE `name` <= 'Beta') AS `position`,
       `name`
FROM `table`
WHERE `name` = 'Beta'
18
zerkms

Si la requête est simple et que la taille du jeu de résultats renvoyé est potentiellement importante, vous pouvez essayer de la scinder en deux requêtes.

La première requête avec un critère de filtrage étroit ne sert qu'à extraire les données de cette ligne, et la seconde requête utilise COUNT avec la clause WHERE pour calculer la position.

Par exemple dans votre cas

Requête 1:

SELECT * FROM tbl WHERE name = 'Beta'

Requête 2:

SELECT COUNT(1) FROM tbl WHERE name >= 'Beta'

Nous utilisons cette approche dans un tableau avec 2M record, ce qui est bien plus évolutif que l'approche d'OMG Ponies.

8
Max

J'ai un problème très très similaire, c'est pourquoi je ne poserai pas la même question, mais je vais partager ici ce que j'ai fait, j'ai dû utiliser également un groupe de et l'ordre d'AVG. Il y a des étudiants, avec signatures et socore, et je devais les classer (en d’autres termes, je calcule d’abord le fichier AVG, puis je les commande dans le DESC, puis, finalement, j’ai besoin d’ajouter le poste (classement pour moi). quelque chose très similaire comme le meilleure réponse ici, avec un peu de changements qui s'adaptent à mon problème):

Je mets enfin la colonne position (rang pour moi) dans le SELECT externe

SET @rank=0;
SELECT @rank := @rank + 1 AS ranking, t.avg, t.name
  FROM(SELECT avg(students_signatures.score) as avg, students.name as name
FROM alumnos_materia
JOIN (SELECT @rownum := 0) r
left JOIN students ON students.id=students_signatures.id_student
GROUP BY students.name order by avg DESC) t 

La position d'une ligne dans la table représente combien de lignes sont "meilleures" que la ligne ciblée.

Donc, vous devez compter ces lignes.

SELECT COUNT (*) + 1 FROM table WHERE name <'Beta'

En cas d'égalité, la position la plus haute est renvoyée.

Si vous ajoutez une autre ligne portant le même nom "Beta" après la ligne "Beta" existante, la position renvoyée serait toujours 2, car elles partageraient la même place dans le classement.

J'espère que cela aidera les personnes qui rechercheront quelque chose de similaire à l'avenir, car je crois que le propriétaire de la question a déjà résolu son problème.

1
NVG

Les autres réponses semblent trop compliquées pour moi.

Voici un exemple simple , disons que vous avez un tableau avec des colonnes:

userid | points

et que vous voulez trier les ID utilisateur par points et obtenir la position de la ligne (le "classement" de l'utilisateur), vous utilisez alors:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, userid, points
FROM
    ourtable
ORDER BY points DESC

num vous donne la position de la ligne (classement).

Si vous avez MySQL 8.0+, vous pouvez utiliser ROW_NUMBER ()

1
Kai Noack

Je parcourais la réponse acceptée et cela semblait un peu compliqué, donc voici la version simplifiée.

SELECT t,COUNT(*) AS position FROM t      
 WHERE name <= 'search string' ORDER BY name
0
Davinder Singh