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.



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 ...


@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'

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.


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.


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.


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 

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

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" = ?)

-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 ..


Diese Antwort löst das Problem hier nicht. Sie könnten in Betracht ziehen, es zu löschen
Damián Rafael Lattenero
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.