MySQL Rangliste vorgänger Nachfolger

Aradiv

Well-known member
ID: 217591
L
20 April 2006
1.683
176
Ich habe folgende Tabelle
uid|punkte
1|637
2|767
3|563
4|928
5|1723
6|123

jetzt möchte ich die Liste nach Punkten absteigend sortieren (soweit kein Problem)
und dann die Position einer ID mit vorgänger und nachfolger ausgeben

also z.B. ID1
soll zurückgeben
uid|punkte
3|563
1|637
2|767

Hatte es so probiert
Code:
SELECT * FROM punkte as p WHERE p.uid=1 
   OR p.uid=(SELECT p2.uid FROM punkte as p2 WHERE p2.punkte<p.punkte ORDER by p2.punkte DESC LIMIT 1) 
   OR p.uid=(SELECT p3.uid FROM punkte as p3 WHERE p3.punkte>p.punkte ORDER BY p3.punkte ASC LIMIT 1) 
ORDER BY punkte DESC LIMIT 3
leider funktioniert das nur wenn ich die p.punkte selbst eintrage (scheint im subquery in der WHERE Bedinung also nicht zur Verfügung zu stehen).

Aradiv
 
leider funktioniert das nur wenn ich die p.punkte selbst eintrage (scheint im subquery in der WHERE Bedinung also nicht zur Verfügung zu stehen).

Kann ja auch nicht funktionieren, denn p.punkte würde sich ja auf genau diejenige Zeile beziehen, die du auch mit p.uid ansprichst, und eine Zeile, die mehr (oder weniger) Punkte hat, als sie selbst, wirst do wohl nicht finden...

Ich würde wahrscheinlich die subqueries eher mit einem Join in weitere Spalten packen...
 
Versuch mal

SELECT * FROM tabelle WHERE UID = userID
UNION ALL
( SELECT * FROM tabelle WHERE PUNKTE > ( SELECT PUNKTE FROM tabelle WHERE UID = userID ) ORDER BY PUNKTE LIMIT 1 )
UNION ALL
( SELECT * FROM tabelle WHERE PUNKTE < ( SELECT PUNKTE FROM tabelle WHERE UID = userID ) ORDER BY PUNKTE DESC LIMIT 1 )
 
Zuletzt bearbeitet:
Oder das hier:
PHP:
SELECT @pkte:=pkte, (SELECT id FROM rangliste WHERE pkte < @pkte ORDER BY pkte DESC LIMIT 1) prev, (SELECT id FROM rangliste WHERE pkte > @pkte ORDER BY pkte LIMIT 1) next, id, pkte FROM rangliste ORDER BY pkte DESC
 
Du kannst beim Join die Spalten selectieren.
Wenn man eine Spalte mit einem Autoincrement dazupacken könnte, so das folgendes entsteht:

Spaltenname UID Punkte
1 3 563
2 1 637
3 2 767

könnte man es einfacher lösen
 
Code:
SELECT * FROM table WHERE uid={uid}
OR uid=(SELECT uid FROM table WHERE punkte<(SELECT punkte FROM table WHERE uid={uid}) ORDER BY day DESC LIMIT 1) 
OR uid=(SELECT uid FROM table WHERE punkte>(SELECT punkte FROM table WHERE uid={uid}) ORDER BY punkte ASC LIMIT 1) ORDER BY punkte DESC

So funktioniert es.

Aradiv
 
Diese Größer-/Kleiner-Lösungen scheitern aber alle bei Gleichstand zweier Teilnehmer ;)
 
arg stimmt dann halt so :ugly:
Code:
SELECT * FROM table WHERE uid={uid} 
OR uid=(SELECT uid FROM table WHERE punkte<=(SELECT punkte FROM table WHERE uid={uid}) AND uid!={uid} ORDER BY day DESC LIMIT 1)  
OR uid=(SELECT uid FROM table WHERE punkte>=(SELECT punkte FROM table WHERE uid={uid}) ORDER BY punkte ASC LIMIT 1) AND uid!={uid} ORDER BY punkte DESC
 
Hm, ich ueberleg mir grad ob ein einfaches LIMIT 3 nicht besser funktioniert. Also du willst element X haben, das heisst du willst eigentlich von X-1 bis X+1 haben, damit sollte folgendes doch eigentlich funktionieren:
Code:
SELECT *
FROM tabelle
LIMIT X-1,3
Dafuer muss man aber die genaue position der uid wissen die du willst, das sollte mit etwas ROW_NUMBER()-magic relativ einfach zu machen sein :D

Ich guck mal weiter ^^
 
wenn 50 Personen den gleichen Punktewert haben und der Nutzer der 48. mit der Punktzahl ist, schlägt das aber fehlt.

Man müsste quasi erstmal die DISTINCT(punkte) von [X-1, X+1] herausfinden und danach auf die realen Nutzer mit der Punktzahl joinen und dann mit PHP filtern, welche der Daten wirklich relevant sind.
Relationale Datenbanken sind nicht wirklich so dolle für diese Art der Operation, wie Aradiv sie möchte, geeignet. Denn ein davor und danach gibt es nicht wirklich.

Alternativ: Wenn jeder dieser Einträge noch einen Zeitstempel hat, könnte das ganze so funktionieren (Person besser als Nutzer):
Code:
SELECT uid, punkte, zeit
FROM punkte
WHERE punkte > $punkte OR (punkte = $punkte AND time < $time)
ORDER BY punkte DESC, time ASC
LIMIT 1
 
Zuletzt bearbeitet:
Ich würde dass offline per Cronjob machen. Einmal die komplette Tabelle einlesen und dann mit PHP oder was auch immer den Vor- und Nachfolger raussuchen und den dann abspeichern.
 
hm ...

Warum fügst Du nicht einfach eine "Zeiger"-spalte dazu?

zeiger|uid|punkte
0|1|637
0|2|767
0|3|563
0|4|928
0|5|1723
0|6|123

Nach dem Sortieren hast Du dann:

zeiger|uid|punkte
0|3|563
0|1|637
0|2|767

Danach lässt Du noch eine "for-next 1 bis Elemente der Tabelle" - Schleife laufen, welche die Nullen der Zeigerspalte durch fortlaufende Nummern ersetzt. Dann hast Du mit zeiger+1 bzw. zeiger-1 den Vorgänger und den Nachfolger.

zeiger|uid|punkte
1|3|563
2|1|637
3|2|767
 
so ausführlich war mein post nicht, aber er sollte von allein drauf kommen.
Wenigstens noch einer der meine Gedanken hatte oder aufgegriffen hat.

Hehe, das Problem wird sein, er weiss nicht, wie er es anstellt, diese Spalte zu erzeugen ;)

Das Problem denk ich ist aber einfacher zu lösen, als das was er wirklich hat