1
Heute musste ich herausfinden, an wievielter Stelle in einer MySQL-Ergebnismenge ein bestimmter Datensatz vorkommt, wobei nach
nicht eindeutigen Feldern sortiert wird, und das möglichst in einer einzigen Abfrage. Da die Lösung recht trickreich ist, möchte ich sie hier einstellen.
Ich habe z.B. die Tabelle "news" mit den Feldern id (INT, PK), entrydate (DATE), sortiere absteigend nach Datum und will wissen, wann id 1 (x-beliebig gewählt) vorkommt.
Ein Weg ist der folgende:
Das ist vergleichbar mit einer While-Schleife als Select, Abbruchbedingung ist das Ende der Datensätze des inneren (zweiten) Selects. Im innersten ersten Select wird die Variable @row mit 0 initialisiert und mit der eigentlichen Zieltabelle news mit dem Alias n gejoint, hier muss die gewünschte Sortierung mit ORDER BY folgen (fett markiert).
Im äußeren Select selektieren wir die bei jedem Datensatz um 1 erhöhte Variable @row als rn.row (rn ist der Alias für die Ergebnismenge des zweiten Selects) und die id der Zieltabelle. Die Felder könnten auch erweitert werden, dazu müssen sie natürlich im Inneren und Äußeren Select zusammen mit dem jeweiligen Alias ergänzt werden.
Die id des Datensatzes, dessen Zeilennummern ermittelt werden soll, steht im abschließenden where und grenzt die Ergebnismenge auf jenen Datensatz ein (fett markiert).
Ergebnis:
Heißt also, der Datensatz mit id=1 kommt in der absteigend nach entrydate sortierten Abfrage an 6. Stelle.
Ich vermute das ist bei richtig großen Ergebnismengen nicht sehr performant, da im inneren SELECT @row=@row+1 AS row... die Ergebnisse erst mal durchnummeriert werden müssen, um dann im äußeren SELECT eingegrenzt zu werden. Natürlich hängt die Geschwindigkeit in dem Fall neben der ollen Hardware auch von den restlichen Daten der Tabelle ab (sind z.B. haufenweise große Blobs drinnen, die man besser ganz vermeidet, bremst das den inneren Select mit Sicherheit).
Ich habs bei Tabellen mit ein paar Tausend Einträgen, vorrangig INT-Spalten, da benotigt die Abfrage laut PHPMyAdmin ~ 0.002s, was für mich ok ist.
Edit: kleine Beispiel-Korrektur, Ergänzung.
nicht eindeutigen Feldern sortiert wird, und das möglichst in einer einzigen Abfrage. Da die Lösung recht trickreich ist, möchte ich sie hier einstellen.
Ich habe z.B. die Tabelle "news" mit den Feldern id (INT, PK), entrydate (DATE), sortiere absteigend nach Datum und will wissen, wann id 1 (x-beliebig gewählt) vorkommt.
id entrydate
16 2010-02-19
17 2010-02-12
11 2010-01-28
4 2010-01-21
14 2010-01-21
1 2010-01-20
5 2010-01-20
Ein Weg ist der folgende:
SELECT rn.row, rn.id FROM (
SELECT @row := @row + 1 as row, n.id
FROM news n, (SELECT @row := 0) r
ORDER BY n.entrydate DESC
) rn WHERE id=1;
Das ist vergleichbar mit einer While-Schleife als Select, Abbruchbedingung ist das Ende der Datensätze des inneren (zweiten) Selects. Im innersten ersten Select wird die Variable @row mit 0 initialisiert und mit der eigentlichen Zieltabelle news mit dem Alias n gejoint, hier muss die gewünschte Sortierung mit ORDER BY folgen (fett markiert).
Im äußeren Select selektieren wir die bei jedem Datensatz um 1 erhöhte Variable @row als rn.row (rn ist der Alias für die Ergebnismenge des zweiten Selects) und die id der Zieltabelle. Die Felder könnten auch erweitert werden, dazu müssen sie natürlich im Inneren und Äußeren Select zusammen mit dem jeweiligen Alias ergänzt werden.
Die id des Datensatzes, dessen Zeilennummern ermittelt werden soll, steht im abschließenden where und grenzt die Ergebnismenge auf jenen Datensatz ein (fett markiert).
Ergebnis:
row id
6 1
Heißt also, der Datensatz mit id=1 kommt in der absteigend nach entrydate sortierten Abfrage an 6. Stelle.
Ich vermute das ist bei richtig großen Ergebnismengen nicht sehr performant, da im inneren SELECT @row=@row+1 AS row... die Ergebnisse erst mal durchnummeriert werden müssen, um dann im äußeren SELECT eingegrenzt zu werden. Natürlich hängt die Geschwindigkeit in dem Fall neben der ollen Hardware auch von den restlichen Daten der Tabelle ab (sind z.B. haufenweise große Blobs drinnen, die man besser ganz vermeidet, bremst das den inneren Select mit Sicherheit).
Ich habs bei Tabellen mit ein paar Tausend Einträgen, vorrangig INT-Spalten, da benotigt die Abfrage laut PHPMyAdmin ~ 0.002s, was für mich ok ist.
Edit: kleine Beispiel-Korrektur, Ergänzung.