MySQL Selecting details using max

為{幸葍}努か 提交于 2019-12-10 17:47:42

问题


I have a set of MySQL tables containing information from a football management game.

The tables are:

  • Players - playerID (PK), playerName
  • Matches - matchID (PK), matchSeason, matchRound, matchType
  • PlayersMatch - playerID, matchID (comp PK), matchRating, playerForm, playerAge, position (can be null)

Data stored in these tables are related to the performance of a player. A Player plays in a match and has a rated performance (matchRating). There is a record in PlayersMatch for each match a player participates in, recording the players current form, match performance, their age at the time of the match (for historical purposes) and the position they played in.

Now, currently, I'm using the following query to list the top 10 players from the whole season (best performance in the season overall as opposed to best performance per round):

SELECT playerID, matchID, playerForm, playerAge, MAX(matchRating)
FROM PlayersMatch
INNER JOIN Matches ON PlayersMatch.matchID = Matches.matchID
WHERE Matches.matchSeason = 35
AND Matches.matchType = 'L'
AND PlayersMatch.position IS NOT NULL
GROUP BY PlayersMatch.playerID
ORDER BY MAX(matchRating) DESC, playerForm ASC

The problem I'm getting is that while I'm getting the right playerID and player matchRating, I'm getting the wrong matchID, form, age and other info (ie they're from other records).

I tried adding the matchID to the group by, and while I got the correct information, I had duplicates as it produced duplicate records for the players (because playerID and matchID make up the PK in PlayersMatch).

Your assistance in this is greatly appreciated.

Edit: After some further reading, I must be writing the SQL incorrectly and that a group by is only going to return the correct information if I just have playerID and max(matchRating) - at the very least to be ANSI SQL correct.

In that case, how do I get the corresponding match details for that performance if I'm using a max/group by?

Edit 2: Looks like I've got a working query:

SELECT * FROM PlayersMatch
INNER JOIN 
   (SELECT playerID, MAX(matchRating)
    FROM PlayersMatch p2
    JOIN Matches
      ON p2.matchID = Matches.matchID
    WHERE matchSeason = 35
    AND matchType = 'L'
    AND p2.position IS NOT NULL
    GROUP BY p2.playerID) AS p1
  ON PlayersMatch.playerID = p1.playerID
  AND PlayersMatch.matchRating = p1.matchRating
JOIN Matches m2
  ON PlayersMatch.matchID = m2.matchID
WHERE m2.matchSeason = 35
AND m2.matchType = 'L'
AND PlayersMatch.position IS NOT NULL
ORDER BY matchRating DESC

The only problem now is that it takes 21 seconds to run. Does this query look correct?


回答1:


Add a second index on PlayersMatch based on MatchID only for your preliminary qualification join to Matches. Add an index to your Matches table on matchSeason and type.

From your edit and posted data samples, I think this resolves to get the first "match" that qualifies the spanned multiple instances under a same "Rank". So, again, the inner-most gets the best MatchRating as your "MAX()" apparently is looking for the HIGHEST Rating. From that, it will immediately re-join to player matches and get the FIRST Match ID for that person with the same Rating. Finally, to close it out, we can directly join to the person for name info, and to the match based on the first match ID found, so no duplicates should be returned... That final result gets sorted per the match ranking..

SELECT STRAIGHT_JOIN
      Players.PlayerName,
      M2.*,
      PM.MatchRating,
      PM.PlayerForm,
      PM.PlayerAge,
      PM.Position
   FROM 
      ( select PreMatch.PlayerID,
               PreMatch.MaxMatch,
               MIN( P3.MatchID ) as FirstMatch
           FROM
               ( SELECT 
                    p2.playerID, 
                    MAX(p2.matchRating) MaxMatch
                 FROM 
                    Matches
                       JOIN PlayersMatch P2
                          ON Matches.MatchID = p2.matchID
                         AND P2.Position is not null
                 WHERE 
                        Matches.MatchSeason = 35
                    AND Matches.MatchType = 'L'
                 GROUP BY
                    p2.playerID ) PreMatch

               JOIN PlayersMatch P3
                   ON PreMatch.PlayerID = P3.PlayerID
                  AND PreMatch.MaxMatch = P3.MatchRating
                  AND P3.Position is not null

                  JOIN Matches M2
                     on P3.MatchID = M2.MatchID
                    AND M2.MatchSeason = 35
                    AND M2.MatchType = 'L' 
          GROUP BY
             PreMatch.PlayerID,
             PreMatch.MaxMatch
      ) AS p1

      JOIN Players
         on P1.PlayerID = Players.PlayerID

      JOIN PlayersMatch PM
          on p1.FirstMatch = PM.MatchID 

   ORDER BY 
      p1.MaxMatch DESC



回答2:


An aggregate only works on the actual column it is applied. It is not record-based. It does select the max-value for the rating, but it does not determine how the other columns are aggregated.

So when you have records:

player 1 | match 1 | 10 
player 1 | match 2 | 5

and you group them by player, it needs to pick only one value for the 'match'-field, but which one is not defined: it doesn't depend on aggregates in other columns.

For what you want to do, you need a subquery:

SELECT p1.playerID, p1.matchID, p1.playerAge, MAX(p1.matchRating)
FROM PlayersMatch P1
JOIN PlayersMatch p2 on p1.id = 
   (SELECT id 
    FROM PlayerMatch p2 
    WHERE p2.playerId = p1.playerId 
    ORDER BY MAX(p2.matchRating) DESC 
    LIMIT 1)
GROUP BY playerID

Note that I've introduced a generated primary key for the playersmatch-table (as I'm not sure on the join-syntax for composite keys and general favor single-field artificial keys.) You can still have a unique constraint on (playerID, matchId).



来源:https://stackoverflow.com/questions/8367770/mysql-selecting-details-using-max

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!