How do you SUM values based on a MIN date? - mysql

Working on an exercise for school trying to calculate the number of points scored by a basketball player ONLY during their first game.
So if I have a table that reports lots of games (Separate rows for 1st and 2nd half) that looks like this:
Game Date Player Half Points
1990-01-01 Mike 1 10
1990-01-01 Mike 2 10
1990-01-03 Mike 1 5
1990-01-03 Ben 2 8
1990-01-05 Kelly 1 4
1990-01-05 Kelly 2 4
1990-01-07 Kelly 1 10
And I want it to end up like this:
Game Date Player Points
1990-01-01 Mike 20
1990-01-03 Ben 8
1990-01-05 Kelly 8
How would I do this?
I have been trying to use the code:
SELECT min(game_Date), player, sum(points);
But it keeps counting points for ALL games, not just points scored during the 1st game, of which there can be one record for the first half and one record for the second.

First you need to find the players' first games, like this
select player, min(game_date) as firstGameDate
from yourtable
group by player
and then get the points in that game by joining to the table again
select yourtable.player, firstgame.firstGameDate, sum(points) as firstGamePoints
from yourtable
inner join
(
select player, min(game_date) as firstGameDate
from yourtable
group by player
) firstgame
on yourtable.player = firstgame.player
and yourtable.game_date = firstgame.firstgameDate
group by yourtable.player, firstgame.firstgameDate
Some varieties of SQL allow you to use ranking functions which could eliminate the need to join to the table itself, but this will work in all varieties.

You have to use your logic. First you have to only grab the first game for each player (inner query). Then from there, you count the points
SELECT t.game_date, t.player, SUM(t.points)
FROM some_table t
JOIN (
SELECT player, MIN(game_date) AS min_date
FROM some_table
GROUP BY player
) a ON a.plyer = t.player AND a.min_date = t.game_date
GROUP BY t.player, t.game_date

Sub query approach of getting the result is given below
Filter the results only getting Player's first games.
Select min(Game_Date),Player from
basketball group by Player;
Use the results from first query to find each player's sum of scores in first game.
Select Game_Date, Player,sum(points) as first_play_points from
basketball where (Game_date,Player) in (Select min(Game_Date),Player
from basketball group by Player) group by Game_Date, Player;
Working fiddle can be found here

Related

Mysql show count on join even if count is 0

Sorry if my title isn't clear. I’m trying to make a lobby system for my card game. When a user presses join game I want the server to check how much space is available in the game they’ve selected then add them to the team with the least players (currently always 1 player on each team but I’d like to expand it to 2 players per team at some point and possibly more than 2 teams).
The tables I have are:
game_teams
game_id, team_id, score
game_players
user_id, game_id, team_id
With game_teams team_id isn't unique or auto-incremented because it seemed like more overhead to have a unique value for every team in every game when they're mostly just there for player placement. So all games have teams 1-2 or 1-3 depending on team count if that makes sense.
The output I’m hoping for is something like:
game_id | team_id | team_size
8 | 1 | 1
8 | 2 | 0
Currently the queries I have are as below but they don’t do what I’m expecting.
This returns all players in team 1 and all players in team 2 ignoring the game id
SELECT games.game_id, COUNT(game_players.user_id) AS team_size, game_teams2.team_id
FROM games
JOIN game_teams2 ON games.game_id=game_teams2.game_id
JOIN game_players ON game_players.team_id=game_teams2.team_id
WHERE games.game_id=1 GROUP BY game_teams2.team_id
This seems to be nearly right but it only returns teams that already have at least 1 player in them where I really need 0 to be returned if the team has no players associated with it.
SELECT game_players.game_id,
COUNT(game_players.user_id) AS team_size, game_players.team_id
FROM game_players WHERE game_players.game_id=8 GROUP BY game_players.team_id
I'm not quite sure what else to try to get the desired output.
Any help would be appreciated.
First, you need to create the distinct set of game_id and team_id from game_teams and then left join it with game_players.
Something like
SELECT x.game_id,
x.team_id,
count(gp.user_id) AS team_size
FROM
(SELECT DISTINCT game_id,
team_id
FROM game_teams
WHERE game_id = 8) x
LEFT JOIN game_players gp ON x.game_id = gp.game_id
AND x.team_id = gp.team_id
You need to take care of nulls for the gp.user_id. I have not tested this, this is just an idea.

Getting last records by date, but for 2 different ID's

I am trying to select the rows that have the last gameDate, todays gameDate (if exists), and the next gameDate from a table called games for any given team(s). It's structure is as follows:
awayTeam homeTeam gameDate
1 2 5/12/16
2 3 5/13/16
3 5 5/14/16
2 4 5/14/16
The problem I am facing is that the teamID can appear in two columns - awayTeam or homeTeam. I saw a lot of solutions that have the group by clause but that only appears to work for just one column. I would like to get the row which corresponds to the last gameDate for each team. For instance, (1,5) would return:
awayTeam homeTeam gameDate
1 2 5/12/15
3 5 5/14/16
Since I want the most recent games, any games from today, and the next game, I was thinking the best way to solve this would be to first get the past rows, then UNION ALL with the rows from todays date, then UNION ALL with the next game. This table is small with only about 3,000 rows. I have the below query, but it only works for homeTeam and not if it appears in awayTeam. Also, the below query takes 2.2 seconds, which seems rediculously high for a table with such a small number of rows.
SELECT g . *
FROM games g
WHERE g.gameDate = (
SELECT MAX( g2.gameDate )
FROM games g2
WHERE g2.homeTeam = g.homeTeam
AND gameDate < NOW( )
AND g.homeTeam
IN (1, 5) )
ORDER BY g.gameDate DESC
I thought about perhaps splitting this into a view so I could easily get the last time a team has played, regardless of whether they appear in the homeTeam or awayTeam column, but that seems like overkill. This is for MySQL. Thanks for your time.
This isn't pretty but it may help you. The inner most derived table gets all the teams in one column along side their dates. The next part gets each teams last game played date. Join that back to the original table and now you have the most recent last game for each team back in the original format. It's confusing to explain but really quite simple if you run each of the selects one by one working from inner to outer.
SQL Fiddle Demo
SELECT yt.*
FROM
(SELECT
Team
, MAX(GameDate) AS GameDate
FROM
(SELECT AwayTeam AS Team, GameDate
FROM YourTable
UNION ALL
SELECT HomeTeam AS Team, GameDate
FROM YourTable) a
WHERE GameDate < NOW()
GROUP BY
Team) MaxDates
JOIN YourTable yt ON (MaxDates.Team = yt.AwayTeam OR MaxDates.Team = yt.HomeTeam)
AND yt.GameDate = MaxDates.GameDate
WHERE MaxDates.team in (1,5)

Mysql Grouping and fetching row corresponding to a maximum field value within that group

I'm confused about how to fit together group and max in mysql. Here's my problem:
I need to group data and then based on a maximum value among that group, I need to fetch that row. Here's a sample:
Table
ID Player Score
1 1 5
2 1 7
3 2 5
4 2 8
5 2 9
After grouping on based of players and fetching all the fields corresponding to maximum score for each player
Result
ID Player Score
2 1 7
5 2 9
Please help me writing the query for this problem.
This is one way to achieve what you want but there could be some easier way:
SELECT s.id,
s.player,
s.score
FROM scores s
JOIN (SELECT id,
player,
MAX(score) AS total
FROM scores
GROUP BY player
) r
ON r.total = s.score AND
r.player = s.player;
Live DEMO
Basically you're comparing the score and the player to get the correct ones as listed in your example.
However if you have multiple entries of the same player with same score you might have a problem there, if you want the first or last it might work with ordering but aside from that it would not work.

MySQL: Total count of points based on weekly vote

So I have this weekly vote for best players of the game. Users can vote for 3 best players, 1st gets 3 points, 2nd 2p and 3rd 1p. So it would be easy just to count all votes of all games and figure out who is the best. But the number of votes per game can be significantly different.
For example:
Vote1:
1st: 100 of total 500 votes -> 20%
Vote2:
1st: 40 of total 100 votes -> 40%
So I would want to count all votes of all games so that every game is equally important. Basically I think I just need to count percentage of votes per game and sum then together. But how can I achieve this easily?
My table is like this:
id, game_id, player3, player2, player1
Here's one way using UNION ALL to put all your players together (if I'm understanding your question correctly) -- not completely sure of your desired results, so have just left like this for now.
select player_id, sum(points)
from (
select player3 player_id, 3 points
from games
union all
select player2, 2
from games
union all
select player1, 1
from games
) t
group by player_id
SQL Fiddle Demo

Returning corresponding columns of a MAX function in MySQL

I have a table that contains the scores bowled by players in a bowling center. Each row has some data about which player bowled the game, which league it was played in, the date, the score of a single game, the lane number, etc.
What I'm trying to do is get who played (and in which league and on what date... Basically the whole row) the best series (three games) on every single lane.
What I have so far is
SELECT PlayerID, LaneNumber, MAX(Series)
FROM (SELECT Season, LeagueName, LaneNumber, WeekNumber, PlayerID, Date, SUM(Score) AS Series
FROM Scores
GROUP BY Season, LeagueName, WeekNumber, PlayerID)
GROUP BY LaneNumber
This works, as in I get the best three games for every single lane, which is actually what I want, but the other field containing the PlayerID isn't actually correct.
In my table, the best score on lane number 24 (gotten from the SUM(Score) and GROUP BY Season, LeagueName, WeekNumber, PlayerID) is 848 and was played by the player that has PlayerID 36.
What I get instead is Lane 24 with 848 (which is correct), but the PlayedID returned is 3166. The same thing happens on every single lane. (As in, I get PlayerIDs that are plain out wrong. And if I had other columns in the first select, they're also wrong)
You are violating the semantics of GROUP BY.
When using GROUP BY, it's only meaningful to SELECT columns that you have grouped by (e.g. LaneNumber) and aggregate functions of the other columns (e.g. MAX(Series)). It is not meaningful to select anything else (in your case, PlayerID) because you don't specify which player ID you want among those that share the same LaneNumber.
Sadly, MySql will by default let you do this without reporting an error, and it will return any value it chooses for the offending column. In your case, this means you are getting back a player ID "randomly" chosen among those that are included in the specified grouping.
You are also doing this in the inner query, where you select LaneNumber, WeekNumber and Date.
Solution
The query needs to be rewritten, but first you need to carefully specify exactly which results you want to get. Do you want the best player and relevant data for each series (and any lane)? For each series and lane separately? The answer to this question will dictate what you need to GROUP BY, and by extension what the query will look like.
As noted by #Jon, you need to remove those elements NOT applicable to specific person. Then, as #Ord had the closest sample, it would be best to pre-query the results into a separate table (not temporary as MySQL will choke on it trying to query from itself in a self-join in the second query).
So, to me (having been a league bowler some years ago), and your content spanning ALL leagues, there would never be two different leagues on the same lane at the same time, however, for a full evening, you could have different leagues starting different times... 6-8:30, 8:45-11 for example... so grouping by the league and date would work. However, you DO need the player as part of the group by to get their respective SUM() values.
To help clarify the answers, let assume I have the following data. This data will represent only a single lane, one week, one season, but two leagues and 3 players per league (for sole purpose of showing results and limiting content here)
League Player Score
L1 1 223
L1 1 218
L1 1 204
L1 2 187
L1 2 201
L1 2 189
L1 3 148
L1 3 152
L1 3 158
L2 4 189
L2 4 195
L2 4 192
L2 5 182
L2 5 199
L2 5 209
L2 6 228
L2 6 234
L2 6 218
CREATE TABLE SeriesScores2
SELECT
Season,
LeagueName,
LaneNumber,
WeekNumber,
PlayerID,
SUM(Score) AS Series
FROM
Scores
GROUP BY
Season,
LeagueName,
LaneNumber,
WeekNumber,
PlayerID;
The first query (above) would create will create the series for all players all weeks, all leagues, etc.. Assume now I've added in the common season, lane, week too
Season League Lane Week Player Series
1 L1 1 1 1 645
1 L1 1 1 2 577
1 L1 1 1 3 458
1 L2 1 1 4 576
1 L2 1 1 5 590
1 L2 1 1 6 680
This gives us the precursor to determining the max(), otherwise we'd have to duplicate the query inside itself and at the outer level making it more complicated than this pre-aggregation.
Now, the above permanent table (can be deleted AFTER getting results), query the FIRST (PreQuery) for the maximum score PER LEAGUE PER LANE... Ex:, its common that a men's league will typically have higher series scores than women... similar with different age groups. So, Men's league Lane 1 highest score and Women's League Lane 1 highest score, etc.. Highest score typically identified by the single week out of the entire season, not highest series per lane every week.
Now, PreQuery "ss" alias is just on the season, league, lane and maximum series. Once THAT is known, self-join to the series score to pull in WHO DID that highest score on said lane and pull the who and what week it occurred
select
ss.season,
ss.leaguename,
ss.lanenumber,
ss.highestSeries,
ss2.PlayerID,
ss2.WeekNumber
from
( select season, leaguename, lanenumber, max( series ) highestSeries
from SeriesScores2
group by season, leaguename, lanenumber ) ss
join SeriesScores2 ss2
on ss.Season = ss2.Season
and ss.LeagueName = ss2.LeagueName
and ss.LaneNumber = ss2.LaneNumber
and ss.HighestSeries = ss2.Series
Now, from the above query... lets break it down. If we take the inner "ss" prequery
( select season, leaguename, lanenumber, max( series ) highestSeries
from SeriesScores2
group by season, leaguename, lanenumber ) ss
We will get the highest scores per league (ex: Men's league vs Women's league on same week, same night, same lane and we find (below), just by max, but don't have the WHO or what week, just the highest series bowled regardless of week or person. So THIS becomes the basis of the JOIN back to the pre-aggregated table "SeriesScores2", yet here, we have the highest series score to ensure we find the correct person
Season League Lane HighestSeries
1 L1 1 645
1 L2 1 680
To refresh preaggregation
Season League Lane Week Player Series
1 L1 1 1 1 645 <-- Join finds THIS entry League 1
1 L1 1 1 2 577
1 L1 1 1 3 458
1 L2 1 1 4 576
1 L2 1 1 5 590
1 L2 1 1 6 680 <-- Join finds THIS entry League 2
So, my original queries did work as I tested them before posting. I don't know what hiccup you had on yours unless column name not correct or something. As with respect to the "Date" column, I didn't particularly care because you had the week number available which would correspond to the week of bowling and would be a 1:1 relationship to a date anyhow. The date column could have been added to the pre-aggregation SeriesScores2 and pull along when getting the person's ID and week. (unless a league bowls on multiple nights in the same week, THEN you would need the explicit date).
Hope this clarifies your questions / comments.
Look here: http://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
It might get messy trying to do it all in one query, but basically, you want to generate your series data as you did:
SELECT Season, LeagueName, LaneNumber, WeekNumber, PlayerID, Date, SUM(Score) AS Series
FROM Scores
GROUP BY Season, LeagueName, WeekNumber, PlayerID
Then, instead of getting max series values from this table, you will want to add a clause: WHERE Series= and then to get the right value, you need to do another select, where you get the max(Series) where LaneNumber is the same in both tables. I would have coded it for you, but I am not confident enough in my MySQL abilities!
Okay, attempting to actually write the MySQL code I was thinking of (I couldn't resist...):
CREATE TEMPORARY TABLE SeriesScores
SELECT Season, LeagueName, LaneNumber, WeekNumber, PlayerID, SUM(Score) AS Series
FROM Scores
GROUP BY Season, LeagueName, WeekNumber, PlayerID;
This bit just gets scores for each series, as you specified in your own MySQL code. Only difference is that I am not selecting Date, because since we are not grouping by it, its value will be random. Then:
SELECT PlayerID, LaneNumber, Series
FROM SeriesScores s1
WHERE Series=(SELECT MAX(s2.Series)
FROM SeriesScores s2
WHERE s1.LaneNumber = s2.LaneNumber);
This bit just selects what you need from SeriesScores, and only considers rows where the series score is the max for that lane.
Does that work for you?