Joignez-vous à une rangée d'une table dans MySQL
-
30-09-2019 - |
Question
J'ai deux tables players
et scores
.
Je veux générer un rapport qui ressemble à quelque chose comme ceci:
player first score points
foo 2010-05-20 19
bar 2010-04-15 29
baz 2010-02-04 13
En ce moment, ma requête ressemble à ceci:
select p.name player,
min(s.date) first_score,
s.points points
from players p
join scores s on s.player_id = p.id
group by p.name, s.points
J'ai besoin du s.points
qui est associé à la ligne qui retourne min(s.date)
. Est-ce que cela se produise avec cette requête? Autrement dit, comment puis-je être certain que je reçois la valeur s.points
correcte pour la ligne jointe?
Note Side: J'imagine que cela est lié en quelque sorte à l'absence de MySQL dense classement. Quelle est la meilleure solution ici?
La solution
Ceci est le plus grand-n-par groupe problème qui revient souvent sur le débordement de pile.
Voici ma réponse habituelle:
select
p.name player,
s.date first_score,
s.points points
from players p
join scores s
on s.player_id = p.id
left outer join scores s2
on s2.player_id = p.id
and s2.date < s.date
where
s2.player_id is null
;
En d'autres termes, score donné s, essayer de trouver un s2 score pour le même joueur, mais avec une date antérieure. Si aucun score antérieure est trouvée, s est plus tôt un.
Re vos commentaires sur les liens: Vous devez avoir une politique pour lequel utiliser en cas d'égalité. Une possibilité est que si vous utilisez les clés primaires auto-incrémentée, celui avec le moins de valeur est la première. Voir le terme supplémentaire dans la jointure externe ci-dessous:
select
p.name player,
s.date first_score,
s.points points
from players p
join scores s
on s.player_id = p.id
left outer join scores s2
on s2.player_id = p.id
and (s2.date < s.date or s2.date = s.date and s2.id < s.id)
where
s2.player_id is null
;
En gros vous devez ajouter des termes de bris d'égalité jusqu'à ce que vous descendez à une colonne qui est garanti d'être unique, au moins pour le joueur donné. La clé primaire de la table est souvent la meilleure solution, mais je l'ai vu des cas où une autre colonne était appropriée.
En ce qui concerne les commentaires que je partageais avec @OMG Ponies, rappelez-vous que ce type de prestations de requête énormement de l'index droit.
Autres conseils
La plupart des SGBDR ne sera même pas vous permettent d'inclure des colonnes non agrégées dans votre clause SELECT lorsque vous utilisez GROUP BY. En MySQL, vous vous retrouverez avec des valeurs de lignes aléatoires pour vos colonnes non agrégées. Ceci est utile si vous avez réellement la même valeur dans une colonne particulière pour toutes les lignes. Par conséquent, il est bien que MySQL ne nous empêche pas, mais il est une chose importante à comprendre.
Un chapitre entier est consacré à cette SQL Antipatterns .