Wie kann ich mit MySQL eine Spalte generieren, die den Datensatzindex in einer Tabelle enthält?

100

Gibt es eine Möglichkeit, die tatsächliche Zeilennummer aus einer Abfrage abzurufen?

Ich möchte in der Lage sein, eine Tabelle namens liga_girl nach einem Feld namens Punktzahl zu ordnen. und geben Sie den Benutzernamen und die tatsächliche Zeilenposition dieses Benutzernamens zurück.

Ich möchte die Benutzer einordnen, damit ich erkennen kann, wo sich ein bestimmter Benutzer befindet, d. H. Joe ist Position 100 von 200, dh

User Score Row
Joe  100    1
Bob  50     2
Bill 10     3

Ich habe hier einige Lösungen gesehen, aber die meisten habe ich ausprobiert, und keine von ihnen gibt tatsächlich die Zeilennummer zurück.

Ich habe das versucht:

SELECT position, username, score
FROM (SELECT @row := @row + 1 AS position, username, score 
       FROM league_girl GROUP BY username ORDER BY score DESC) 

Wie abgeleitet

... aber es scheint nicht die Zeilenposition zurückzugeben.

Irgendwelche Ideen?

TheBounder
quelle
Ist row ein Dateiname oder möchten Sie nach Primärschlüssel bestellen?
Sarfraz
In SQL sind Zeilennummern wirklich nicht wichtig. Sie sollten Ihrer Tabelle einen Primärschlüssel für die automatische Inkrementierung hinzufügen.
Simendsjo
9
Der Primärschlüssel sollte NIEMALS eine Zeilenkennung enthalten, da diese für die tatsächliche Zeilenposition nicht zuverlässig sind.
TheBounder
3
Da die Zeilennummer eine Funktion der Punktzahl ist, von der ich annehme, dass sie kein statischer Wert ist, würde eine automatische Inkrementierung (Primärschlüssel oder nicht) nicht das beabsichtigte Ergebnis liefern.
Kasperjj
Vielleicht möchten Sie das Hässliche für eine benutzerdefinierte Funktion speichern
AdrianBR

Antworten:

174

Möglicherweise möchten Sie Folgendes ausprobieren:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r;

Der JOIN (SELECT @curRow := 0)Teil ermöglicht die Variableninitialisierung, ohne dass ein separater SETBefehl erforderlich ist .

Testfall:

CREATE TABLE league_girl (position int, username varchar(10), score int);
INSERT INTO league_girl VALUES (1, 'a', 10);
INSERT INTO league_girl VALUES (2, 'b', 25);
INSERT INTO league_girl VALUES (3, 'c', 75);
INSERT INTO league_girl VALUES (4, 'd', 25);
INSERT INTO league_girl VALUES (5, 'e', 55);
INSERT INTO league_girl VALUES (6, 'f', 80);
INSERT INTO league_girl VALUES (7, 'g', 15);

Testabfrage:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r
WHERE   l.score > 50;

Ergebnis:

+----------+----------+-------+------------+
| position | username | score | row_number |
+----------+----------+-------+------------+
|        3 | c        |    75 |          1 |
|        5 | e        |    55 |          2 |
|        6 | f        |    80 |          3 |
+----------+----------+-------+------------+
3 rows in set (0.00 sec)
Daniel Vassallo
quelle
19
Sie können auch initialisieren, @curRowindem Sie die JOINAnweisung durch ein Komma ersetzen , wie folgt:FROM league_girl l, (SELECT @curRow := 0) r
Mike
2
@ Mike: Das stimmt. Und das ist wahrscheinlich auch ordentlicher. Vielen Dank für diesen Tipp.
Daniel Vassallo
2
@smhnaji MySQL erfordert, dass jeder "abgeleiteten Tabelle" ein Name gegeben wird. Ich habe beschlossen, es "r" zu nennen :) ... In diesem Fall hat es wenig Sinn, aber Sie würden es normalerweise verwenden, um auf Attribute der abgeleiteten Tabelle zu verweisen, als wäre es eine echte Tabelle.
Daniel Vassallo
20
Die Benutzer sollten sich darüber im Klaren sein, dass diese Zeilennummer vor jeder Bestellung berechnet wird , sodass die Nummern durcheinander geraten können, wenn die Reihenfolge die Zeilenreihenfolge ändert.
Grimmig ...
7
Gibt es eine Möglichkeit, diese Zeilennummer nach dem zu berechnen ORDER BY?
Pierre de LESPINAY
38
SELECT @i:=@i+1 AS iterator, t.*
FROM tablename t,(SELECT @i:=0) foo
Peter Johnson
quelle
Gibt es eine Möglichkeit, dies so zu tun, dass die Iteratorspalte eine Ganzzahl und keine Dezimalzahl ist?
Kraftydevil
7

Hier kommt die Struktur der Vorlage, die ich verwendet habe:

  select
          /*this is a row number counter*/
          ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
          as rownumber,
          d3.*
  from 
  ( select d1.* from table_name d1 ) d3

Und hier ist mein Arbeitscode:

select     
           ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
           as rownumber,
           d3.*
from
(   select     year( d1.date ), month( d1.date ), count( d1.id )
    from       maindatabase d1
    where      ( ( d1.date >= '2013-01-01' ) and ( d1.date <= '2014-12-31' ) )
    group by   YEAR( d1.date ), MONTH( d1.date ) ) d3
Goadreamer
quelle
Perfekt, funktioniert wie Charme und Vorlage kann mit Unterabfrage als Parameter wiederverwendet werden.
Zavael
Diese Lösung funktioniert auch, wenn Ihre Basisabfrage GROUP BY
Dave R
4

Sie können auch verwenden

SELECT @curRow := ifnull(@curRow,0) + 1 Row, ...

um die Zählervariable zu initialisieren.

Feuerstelle
quelle
3
@curRowMöglicherweise haben Sie noch einen Wert aus dem letzten Mal, als Sie diese Abfrage in der aktuellen Sitzung ausgeführt haben.
Bill Karwin
True, aber nur, wenn Sie dieselbe Verbindungsinstanz erneut abfragen. Lokale Variablen werden automatisch entsorgt, sobald die Verbindung geschlossen wird.
Herd
Ja, das habe ich gemeint, als ich "in der aktuellen Sitzung" sagte.
Bill Karwin
3

Angenommen, MySQL unterstützt dies, können Sie dies problemlos mit einer Standard-SQL-Unterabfrage tun:

select 
    (count(*) from league_girl l1 where l2.score > l1.score and l1.id <> l2.id) as position,
    username,
    score
from league_girl l2
order by score;

Bei großen Mengen angezeigter Ergebnisse ist dies etwas langsam und Sie möchten stattdessen zu einem Self-Join wechseln.

ftzdomino
quelle
3

Wenn Sie nur die Position eines bestimmten Benutzers nach der Reihenfolge nach Feldbewertung wissen möchten, können Sie einfach alle Zeilen aus Ihrer Tabelle auswählen, in denen die Feldbewertung höher ist als die aktuelle Benutzerbewertung. Verwenden Sie die zurückgegebene Zeilennummer + 1, um die Position dieses aktuellen Benutzers zu ermitteln.

Angenommen, Ihre Tabelle league_girlund Ihr primäres Feld sind id, können Sie Folgendes verwenden:

SELECT count(id) + 1 as rank from league_girl where score > <your_user_score>
Heryno
quelle
0

Ich fand die ursprüngliche Antwort unglaublich hilfreich, wollte aber auch eine bestimmte Reihe von Zeilen basierend auf den von mir eingefügten Zeilennummern erfassen. Aus diesem Grund habe ich die gesamte ursprüngliche Antwort in eine Unterabfrage eingeschlossen, damit ich auf die von mir eingefügte Zeilennummer verweisen kann.

SELECT * FROM 
( 
    SELECT *, @curRow := @curRow + 1 AS "row_number"
    FROM db.tableName, (SELECT @curRow := 0) r
) as temp
WHERE temp.row_number BETWEEN 1 and 10;

Eine Unterabfrage in einer Unterabfrage zu haben, ist nicht sehr effizient. Es lohnt sich daher zu testen, ob Sie ein besseres Ergebnis erzielen, wenn Ihr SQL Server diese Abfrage verarbeitet oder die gesamte Tabelle abruft und die Anwendung / der Webserver die Zeilen nachträglich bearbeitet .

Persönlich ist mein SQL Server nicht übermäßig ausgelastet, daher war es vorzuziehen, die verschachtelten Unterabfragen zu verarbeiten.

BasicExp
quelle
0

Ich weiß, dass das OP um eine mysqlAntwort bittet , aber da ich festgestellt habe, dass die anderen Antworten für mich nicht funktionieren,

  • Die meisten von ihnen scheitern mit order by
  • Oder sie sind einfach sehr ineffizient und machen Ihre Anfrage für eine fette Tabelle sehr langsam

Um Zeit für andere wie mich zu sparen, indizieren Sie die Zeile einfach, nachdem Sie sie aus der Datenbank abgerufen haben

Beispiel in PHP:

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1;
}

Beispiel in PHP mit Offset und Limit für Paging:

$limit = 20; //page size
$offset = 3; //page number

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1+($limit*($offset-1));
}
Azerafati
quelle