Anzeige des aktuellen Platz in einer Rally

Greaseball100

Well-known member
ID: 105292
L
4 Juli 2006
118
10
Hallo.

Ich suche eine Möglichkeit dem User seinen aktuellen Platz in einer Rally (Klickrally, Refrally etc.) anzeigen zu lassen a la

Sie haben x Punkte und befinden sich momentan auf dem xten Platz
.

Momentan lass ich das ganze mit ner While-Schleife orgeln, aber das ist ja auch nicht das gelbe vom Ei.

Wie immer, bitte keine fertigen Lösungen sondern dezente Hinweise mit dem Zaunpfahl.

Danke.
 
Wieso sollte das nicht das gelbe vom Ei sein? Am einfachsten so denk ich zumindest einen Query mit order by punkte limit 0, Plätze insgesamt. Und das ganze dann mit ner While durchgehen, durch das order by vom Query hast Deine Plätze. Man könnte dies nun noch optimieren das man trotz des Limits eine for nimmt und die $x ist dann Dein Platz, aber ne temporäre Vars tuts inner while auch denke ich. Einfach echo ++$x; oder sowas. Natürlich vorher irgendwo $x=0;
 
Code:
SELECT COUNT(*) + 1 AS platz
FROM user
WHERE rallypoints < (SELECT rallypoints FROM user WHERE id = 93995)

würde bestimmt auch mit nem schnelleren Join gehen, aber da habe ich gerade keine Lust zu überlegen
 
@strolch00

Mir geht es nicht darum, die ersten 10 Plätze anzuzeigen.

Sondern es geht um die Plätze danach.
Ich geb mal ein optisches Beispiel.

1. User1 10000 Punkte
2. User2 8000 Punkte
3. User3 5000 Punkte
4. User4 4500 Punkte
5. User5 4000 Punkte
6. User6 3500 Punkte
7. User7 3000 Punkte
8. User8 2500 Punkte
9. User9 1500 Punkte
10. User10 500 Punkte

Sie haben 30 Punkte und befinden sich in der Rally auf dem 32ten Platz

Mir gehts es allein um den letzten Satz.
Das davor ist uninteresant.
Kann man das ganze mit nur einem Query lösen und wenn ja wie?
 
Okay, da hab ich mich von strolch wohl irritieren lassen und da war was anderes gemeint :ugly:
Aber ice-breaker hats ja schon gepostet :)
 
Code:
SELECT COUNT(*) AS platz 
FROM user 
WHERE rallypoints > (SELECT rallypoints FROM user WHERE id = 93995)

So wäre es dann richtig.
Bei dem Query muss es größer als heißen und nicht kleiner als sonst werden nur die Plätze gezählt die unter einem sind und das +1 kann auch weg.

Danke für die Hilfe.
 
Na, das +1 muss schon dazu


Code:
SELECT COUNT(*) AS platz 
FROM user 
WHERE rallypoints > (SELECT rallypoints FROM user WHERE id = 93995)

wenn Du z.B. der bist mit den meisten Punkten, dann ergibt der COUNT(*) den Wert Null, aber Du bist nicht auf Platz Null, sondern auf Eins.

ausser natürlich, Du hast
Code:
WHERE rallypoints >= (SELECT rallypoints FROM user WHERE id = 93995)

geschrieben, dann käme schon Eins raus, dann hast Du aber Probleme, wenn mehrere punktgleich sind
 
Code:
SELECT COUNT(*) + 1 AS platz
FROM user
WHERE rallypoints < (SELECT rallypoints FROM user WHERE id = 93995)
würde bestimmt auch mit nem schnelleren Join gehen, aber da habe ich gerade keine Lust zu überlegen
Ein Auto-Join, wo die Kleiner-Gleich-Bedingung als JOIN-Bedinung benutzt wird. Kleiner-Gleich spart das +1 am COUNT(*). Es gibt nur eine Person, die mehr oder gleich viele Punkte als sie selbst hat: sie selbst, also Rang 1.
Ich hätte in jedem Fall das Problem wenn mehrere punktgleich sind.
Das ist kein "Problem", sondern ne Frage von "Was willst du?".

Platz|Punkte||Platz|Punkte||Platz|Punkte||Platz|Punkte
1|5.000||1|5.000||1|5.000||1|5.000
2|4.000||3|4.000||2|4.000||2|4.000
3|4.000||3|4.000||2|4.000||2|4.000
4|3.000||4|3.000||4|3.000||3|3.000
 
Egal. Ob du von links nach rechts oder von rechts nach links liest ;)
 
Du meinst also ein

Code:
SELECT COUNT(*) 
FROM user A , user B
WHERE A.rallypoints <= B.rallypoints
AND A.id = 93995 ;


Du hast recht, es ist kleiner-gleich.


Wäre interessant zu wissen, ob diese Variante performanter ist als die uncorrelated subquery. In der Regel sind die Joins ja schneller aber das könnte die berühmte Ausnahme sein
 
So, ich habe mich da mal drangesetzt, es kam eine Überraschung und ein ziemlich erwartetes Ergebnis heraus:

Benchmark-Grundlagen:
Code:
CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) unsigned NOT NULL,
  `rallypoints` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `rallypoints` (`rallypoints`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

IDs des Zahlenbereichs [100,100.000] und Rallypoints in dem Zahlenbereich [1, 88.1310.000]
Clustering der Datensätze nach der id aufsteigend.

der Test findet auf einem MySQL 5.1.41 mit dem Betriebssystem Windows XP statt und alle Daten passen in den Ram (auch wenn dies irrelevant ist, wie sich noch zeigen wird)

zu betrachtender Datensatz:
ID = 47427 mit rallypoints = 792953690 und somit Platz 97

Query 1:
Code:
SELECT COUNT(*) + 1 AS platz 
FROM user 
WHERE rallypoints > (SELECT rallypoints FROM user WHERE id = 47427)

Query 2:
Code:
SELECT COUNT(*) 
FROM user A, user B
WHERE A.rallypoints <= B.rallypoints
AND A.id = 47427

Ergebnisse:

Explain Query1:
Code:
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: user
         type: range
possible_keys: rallypoints
          key: rallypoints
      key_len: 4
          ref: NULL
         [B]rows: 114[/B]
        Extra: Using where; Using index
*************************** 2. row ***************************
           id: 2
  select_type: SUBQUERY
        table: user
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref:
         rows: 1
        Extra:
2 rows in set (0.00 sec)

Explain Query2:
Code:
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: A
         type: const
possible_keys: PRIMARY,rallypoints
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra:
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: B
         type: index
possible_keys: rallypoints
          key: rallypoints
      key_len: 4
          ref: NULL
         [B]rows: 1000[/B]
        Extra: Using where; Using index
2 rows in set (0.00 sec)

Fazit:
Erstaunlicherweise hat der MySQL Query Optimizer den Subselect korrekt verarbeitet und nicht wie in älteren Versionen einen Join daraus gebastelt.
Es wird ein Datensatz für den Subquery abgerufen und geschätzte 114 um das Ergebnis zu bestimmen (perfekt!).

Bei theHackers und transversalis Query fällt das Urteil leider niederschmetternd aus, MySQL kann für den kleiner-gleich-Check des Joins scheinbar den rallypoint-Index nicht nutzen, weshalb viel mehr rows betrachtet werden müssen.
 
Hey, danke für die Auswertung. Dann hat es sich ja bestätigt, als ich vermutet habe, dass es sich hier um die berühmte Ausnahme von der Regel handelt.

Ein Index über die Rallypoints wäre allerdings sowieso fragwürdig, weil sich dieser Wert permanent ändern würde und Indices über sich dauernd verändernde Werte suboptimal sind.

Dass der Optimizer eine uncorrelated subquery in einen join umwandelt, würde mich allerdings überraschen. Ich dachte, das geht nur bei correlated subqueries. Aber wie schon erwähnt, den Optimizer von mySql kenn ich nicht so gut.
 
Ein Index über die Rallypoints wäre allerdings sowieso fragwürdig, weil sich dieser Wert permanent ändern würde und Indices über sich dauernd verändernde Werte suboptimal sind.
ich bin von batch-updates auf die rallypoints ausgegangen, also nicht bei jedem Spiel sondern alle x Zeitspanne.

Dass der Optimizer eine uncorrelated subquery in einen join umwandelt, würde mich allerdings überraschen. Ich dachte, das geht nur bei correlated subqueries. Aber wie schon erwähnt, den Optimizer von mySql kenn ich nicht so gut.
der von MySQL ist miserabel ;) Die erste Zeit wurden Subquerys einfach immer zu Joins gemacht, damit man erzählen kann, man unterstützt Subquerys :LOL: