MySQL erhält die Zeilenposition in ORDER BY

86

Mit der folgenden MySQL-Tabelle:

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

Wie kann ich eine wählen einzelne Zeile und seine Position unter den anderen Zeilen in der Tabelle, wenn nach sortiert name ASC. Wenn die Tabellendaten also so aussehen, wenn sie nach Namen sortiert sind:

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

Wie kann ich die BetaZeile auswählen, die die aktuelle Position dieser Zeile erhält? Die Ergebnismenge, nach der ich suche, wäre ungefähr so:

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

Ich kann einfach SELECT * FROM tbl ORDER BY name ASCdie Zeilen in PHP aufzählen, aber es scheint verschwenderisch, eine potenziell große Ergebnismenge nur für eine einzelne Zeile zu laden.

Leepowers
quelle
stackoverflow.com/questions/2520357/…
Ciro Santilli 6 冠状 冠状 病. 法轮功
Mögliches Duplikat von MySQL - Holen Sie sich die Zeilennummer auf select
jberryman

Antworten:

120

Benutze das:

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'

... um einen eindeutigen Positionswert zu erhalten. Dies:

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

... gibt Krawatten den gleichen Wert. IE: Wenn es zwei Werte an zweiter Stelle gibt, haben beide eine Position von 2, wenn die erste Abfrage einem von ihnen eine Position von 2 und dem anderen eine Position von 3 gibt ...

OMG Ponys
quelle
@actual: Es gibt nichts zu sagen - es gibt keine Alternative, außer zu einem Konkurrenten zu wechseln, der Analysefunktionen unterstützt (PostgreSQL, Oracle, SQL Server, DB2 ...)
OMG Ponies
2
@OMGPonies Vergiss einfach ein Komma danach position, aber es ist perfekt.
Pierallard
Ich weiß, dass es ein ziemlich alter Beitrag ist, aber ich brauche eine ähnliche Lösung. Wenn Sie jedoch innere Verknüpfungen verwenden, gruppieren nach und sortieren nach, ignoriert das Feld "Position" diese und der Wert wird verwechselt. Irgendwelche Lösungen?
Daniel
@ Daniel, du solltest eine neue Frage stellen und dich vielleicht auf diese beziehen.
PeerBr
@actual, da dies eine Abfrage für eine einzelne Zeile ist, sollte hier kein wesentliches Leistungsproblem bestehen. Es ist anders, wenn Sie versuchen, die Ränge einer vollständigen Liste zu erreichen, aber Sie könnten einfach "schummeln" und einen impliziten Rang verwenden, indem Sie nach Punkten ordnen.
AgmLauncher
20

Dies ist der einzige Weg, den ich mir vorstellen kann:

SELECT `id`,
       (SELECT COUNT(*) FROM `table` WHERE `name` <= 'Beta') AS `position`,
       `name`
FROM `table`
WHERE `name` = 'Beta'
zerkms
quelle
2
+1 Netter Trick ... Allerdings möchten Sie wahrscheinlich name <= 'Beta'stattdessen verwenden
Daniel Vassallo
Dieser Ansatz liefert die gleichen positionWerte für Bindungen.
OMG Ponys
(Meinen vorherigen Kommentar gelöscht - ich habe mich geirrt) ... Was ist, wenn Sie dort einen hinzufügen LIMIT 1? Im Falle eines Unentschieden erhalten Sie nur eine Reihe mit der letzten Position des Unentschieden.
Daniel Vassallo
Wenn OP garantieren kann, dass das nameFeld eindeutig ist, gibt es keinen Grund, die Abfrage komplexer zu gestalten. Wenn er nicht kann - dann warten wir auf seine Ergebniserwartungen für gebundene Namen.
Zerkms
8

Wenn die Abfrage einfach ist und die Größe der zurückgegebenen Ergebnismenge möglicherweise groß ist, können Sie versuchen, sie in zwei Abfragen aufzuteilen.

Die erste Abfrage mit einem eingegrenzten Filterkriterium, nur um Daten dieser Zeile abzurufen, und die zweite Abfrage verwendet die Klausel COUNTwith WHERE, um die Position zu berechnen.

Zum Beispiel in Ihrem Fall

Abfrage 1:

SELECT * FROM tbl WHERE name = 'Beta'

Abfrage 2:

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

Wir verwenden diesen Ansatz in einer Tabelle mit 2 Millionen Datensätzen und dies ist weitaus skalierbarer als der Ansatz von OMG Ponies.

Max
quelle
4

Die anderen Antworten scheinen mir zu kompliziert.

Hier ein einfaches Beispiel : Angenommen, Sie haben eine Tabelle mit Spalten:

userid | points

und Sie möchten die Benutzer-IDs nach Punkten sortieren und die Zeilenposition (das "Ranking" des Benutzers) erhalten, dann verwenden Sie:

SET @row_number = 0;

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

num gibt Ihnen die Zeilenposition (Rangfolge).

Wenn Sie MySQL 8.0+ haben, möchten Sie möglicherweise ROW_NUMBER () verwenden.

Kai Noack
quelle
2

Die Position einer Zeile in der Tabelle gibt an, wie viele Zeilen "besser" sind als die Zielzeile.

Sie müssen diese Zeilen also zählen.

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

Bei einem Unentschieden wird die höchste Position zurückgegeben.

Wenn Sie nach der vorhandenen Zeile "Beta" eine weitere Zeile mit dem gleichen Namen "Beta" hinzufügen, ist die zurückgegebene Position immer noch 2, da sie denselben Platz in der Klassifizierung haben.

Ich hoffe, dies hilft Menschen, die in Zukunft nach etwas Ähnlichem suchen werden, da ich glaube, dass der Frageninhaber sein Problem bereits gelöst hat.

NVG
quelle
2

Ich habe ein sehr sehr ähnliches Problem, deshalb werde ich nicht die gleiche Frage stellen, aber ich werde hier mitteilen, was ich getan habe. Ich musste auch eine Gruppe von verwenden und von AVG bestellen. Es gibt Studenten mit Unterschriften und Socore, und ich musste sie bewerten (mit anderen Worten, ich berechnete zuerst die AVG, bestellte sie dann in DESC und schließlich musste ich die Position hinzufügen (Rang für mich), also tat ich es etwas sehr ähnliches wie die beste Antwort hier, mit ein paar Änderungen, die sich an mein Problem anpassen):

Ich habe schließlich die positionSpalte (Rang für mich) in das externe SELECT eingefügt

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 
Damián Rafael Lattenero
quelle
Diese Antwort war leichter zu verstehen als die akzeptierte. +1
Eric Seastrand
1

Ich habe die akzeptierte Antwort durchgesehen und sie schien etwas kompliziert zu sein. Hier ist die vereinfachte Version davon.

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

Ich habe ähnliche Arten von Problemen, bei denen ich den Rang ( Index ) der Tabelle benötige order by votes desc. Das Folgende funktioniert gut für mich.

Select *, ROW_NUMBER() OVER(ORDER BY votes DESC) as "rank"
From "category_model"
where ("model_type" = ? and "category_id" = ?)
Bedram Tamang
quelle
-9

Möglicherweise benötigen Sie eine Add-Syntax

LIMIT

also benutze

SELECT * FROM tbl ORDER BY name ASC LIMIT 1

wenn Sie nur eine Reihe brauchen ..

Eka Rudito
quelle
Diese Antwort löst das Problem hier nicht. Sie könnten in Betracht ziehen, es zu löschen
Damián Rafael Lattenero