2010-12-12 3 views
2

J'ai le tableau suivant la tenue de l'argent virtuel pour les tournois hebdomadaires:PostgreSQL: comment trouver id avec de l'argent max dans une table hebdomadaire

# select * from pref_money limit 5; 
     id  | money | yw 
----------------+-------+--------- 
OK32378280203 | -27 | 2010-44 
OK274037315447 | -56 | 2010-44 
OK19644992852 |  8 | 2010-44 
OK21807961329 | 114 | 2010-44 
FB1845091917 | 774 | 2010-44 
(5 rows) 

Dans mes utilisateurs liste script PHP, je dois savoir qui a été gagnant hebdomadaire, de sorte que je peux montrer des médailles près de leurs noms. J'essaie donc cette instruction SQL:

# find the weekly winners 
$sth = $db->prepare('select id from pref_money 
    where money in 
    (select max(money) from pref_money group by yw)'); 

$sth->execute(); 
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) 
     @$medals[$row['id']]++; 

Cela fonctionne la plupart du temps, mais parfois j'obtenir faux positifs, lorsqu'un utilisateur arrive à avoir la même quantité d'argent dans une semaine (mais pas là max) comme le gagnant dans une autre semaine.

Quelqu'un a-t-il une idée sur la façon de changer l'instruction SQL, de sorte que seuls les identifiants gagnants sont réellement sélectionnés?

Lorsque je tente ce qui suit, je reçois une erreur:

# select id, max(money) from pref_money group by yw; 
ERROR: column "pref_money.id" must appear in the 
    GROUP BY clause or be used in an aggregate function 

Merci! Alex

MISE À JOUR: PostgreSQL 8.4.5/CentOS et il peut y avoir des liens :-)

Répondre

3

Quelle version PostgreSQL? C'est facile avec des fonctions de fenêtrage introduites dans 8.4:

select id, yw from (
    select id, 
      yw, 
      row_number() over(partition by yw order by money desc) as ranking 
    from pref_money 
) x 
where x.ranking = 1 

Cela à peu près faire dans les anciennes versions:

select id, yw 
from pref_money 
    join (select yw, max(money) max_money 
      from pref_money 
      group by yw) max_money 
      on pref_money.yw = max_money.yw and pref_money.money = max_money.max_money 

Mais observer que les semaines où deux utilisateurs sont à égalité pour le maximum d'argent seront présentés deux fois dans le résultat. Si tel est effectivement ce que vous voulez, vous pouvez faire avec les fonctions de fenêtrage aussi, à l'aide rank() au lieu de row_number()

+0

+1: Battez-moi de 6 secondes! –

+0

Merci, mais le 2ème code ne fonctionne pas vraiment - affiche toutes les lignes de la table pref_money –

+0

oups ... essayant de le ranger brisé la condition de jointure. – araqnid

2

En supposant PostgreSQL 8.4+, et personne ne liens pour la position de gagnant dans une semaine donnée - ce sera la liste hebdomadaire gagnants:

WITH sample AS (
    SELECT t.id, 
     t.yw, 
     ROW_NUMBER() OVER(PARTITION BY t.yw 
           ORDER BY t.money DESC) AS rank 
    FROM PREF_MONEY t) 
SELECT s.id 
    FROM sample s 
WHERE s.rank = 1 

S'il peut y avoir des liens, utilisez:

WITH sample AS (
    SELECT t.id, 
     t.yw, 
     DENSE_RANK() OVER(PARTITION BY t.yw 
           ORDER BY t.money DESC) AS rank 
    FROM PREF_MONEY t) 
SELECT s.id 
    FROM sample s 
WHERE s.rank = 1 

I inclus la colonne yw, dans le cas où vous voulez/besoin de filtrer par la colonne.

Questions connexes