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

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)

Related

How do you SUM values based on a MIN date?

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

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.

Sum values across multiple tables

I'm having trouble trying to sum a field GROUPED BY a common user ID from TWO DIFFERENT tables.
To give you a little more info... I am trying to track player performances by date (i.e.: most points scored on ALL Nov. 14's). The database is split, one table for regular season games and one table for playoffs. So, for example, a player may have played a regular season game on May 3, 2001....but a playoff game on May 3, 2005.
So, I'm trying to build a view with the sums of every player on all dates.
What I have for a single table:
SELECT PlayerId,sum(Points) as TOT_PTS
FROM RS_games
WHERE DAY(Date)=$cur_day
AND MONTH(Date)=$cur_month
GROUP BY PlayerId
...but I can't figure how I could sum the values of each player across two tables without creating a third view as a "stepping stone".
Any ideas?
If you want the results by (DAY-MONTH) you can do:
SELECT playerID,
CONCAT (DAY(DATE), '-', MONTH(DATE)) AS DAY_MONTH,
SUM(points) AS Total_Points
FROM (
SELECT playerID, DATE, points
FROM rs_games
UNION ALL
SELECT playerID, DATE, points
FROM po_games
) a
GROUP BY 1, 2
sqlfiddle demo
This way, you would end up with a result with every playerId,dd-mm, and the sum of points that were scored in that specific day across the years.
Just to lay out what I was saying:
select
ALL_players.PlayerID as PlayerID
COALESCE(reg.Points, 0) + COALESCE(po.Points, 0) as Points
from
ALL_players
left join
(select PlayerID, DATE(Date) as Date, sum(Points) as Points
from RS_games
WHERE DAY(Date)=$cur_day AND MONTH(Date)=$cur_month
group by PlayerID) as reg
on reg.PlayerID = ALL_players.PlayerID
left join
(select PlayerID, DATE(Date) as Date, sum(Points) as Points
from PO_games group by DATE(Date), PlayerID
WHERE DAY(Date)=$cur_day AND MONTH(Date)=$cur_month
group by PlayerID) as po
on po.PlayerID = ALL_players.PlayerID
EDIT: Looking again at requirements this will need either a full outer join or some adjustment... adjusting (should be working now)

Perform a mathematical function on a JOIN of 2 tables - each with a COUNT function

I'm trying to accomplish the following-
I have 2 tables for soccer teams (not created by me, this is what I have to work with):
won_matches-
columns: team_id | match_name | scored_goals
lost_matches-
columns: team_id | match_name | scored_goals
teams_names-
team_id | team_name
(I don't care about the match name or the number of scored goals)
What I need to do is COUNT how many entries each team has in the won_matches table and how many entries it has in the lost_matches table, and then divide the number of lost_matches by the number of won_matches, thus getting a lost/won matches ratio.
I then need to present this ratio for each team (or all teams) along with its team name.
I tried somethings like this, but it doesn't work as needed:
SELECT b. team_name, (SELECT COUNT(team_id)
FROM won_matches [***optional; WHERE team_id=37***]) / COUNT(a.team_id)*100 AS lost_won_ratio
FROM lost_matches a
join teams_names b on a.team_id=b.team_id
[***optional; WHERE a.team_id=37***]
Would be grateful for your suggestions.
Something like this should work.
SELECT tn.teamID, sum(won_matches.teamID ) as WON, sum(lost_matches.teamID ) as LOST,(sum(won_matches.teamID )/sum(lost_matches.teamID )) as WLratio
From teams_names AS tn LEFT JOIN won_matches ON tn.teamID = won_matches.teamID LEFT JOIN lost_matches ON tn.teamID = lost_matches.teamID
Try something like this:
select team_id, Won, count(*) as Matches, sum(scored_goals) as Goals
from
(select 1 as Won, Team_id, scored_goals from won_matches
union all
select 0 as Won, team_id, scored_goals from lost_matches) x
group by team_id, Won
I think something like that will do the trick:
select team_id,
count(won), count(lost),
count(won)/(count(won)+count(lost)) as 'Win ratio' from
(
select True as won, NULL as lost, won_matches.* from won_matches
union all
select NULL as won, True as lost, lost_matches.* from lost_matches
) as S group by team_id
http://sqlfiddle.com/#!2/6dbaf/2 (EDIT: use a join to display team name)
Please notice I don't take into account possible drawn matches since I don't know how this is stored in your DB.
EDIT Notice as well I used count(won)/(count(won)+count(lost)) as count ratio formula. That seems more logical. If you stick with count(lost)/count(win) you will have to deal with the divide by 0 case...

Show biggest margin between 1st and 2nd within group

Name Day Points
Brian 1 6
Tom 1 11
Freddy 1 7
Kim 2 10
Sandra 2 1
Brian 2 3
I need to know who has won with the biggest margin to number two - but only between people on the same day.
Thus if done properly it would tell me Kim has won by the biggest margin.
I don't quite know how to handle on this one.
select
first_place.name,
max_points-max(points) as max_margin
from the_table
inner join
(select name, day, max(points) as max_points
from the_table group by day) as first_place
on the_table.day=first_place.day
where the_table.points<max_points
group by the_table.day
order by max_margin desc limit 1 ;
This would need to be done with two sub queries... Inner most to get the highest score for a single day, then, find the next highest scrore under the first place position, then find the margin... However, due to your sample data of just names, no consideration for unique names which would otherwise be by some internal ID... Say "Brian" in your sample data... is it the same Brian on both days, or is it a different person. Additionally, what if two people are tied for first place with 11 points, then my query would show BOTH people in first place before the margin to the now "3rd" place person as the detected margin. You will probably have to modify some to accommodate such conditions described..
SELECT
FS.Day,
FS.FirstPlace,
FS.SecondPlace,
FS.FirstPlace - FS.SecondPlace as Margin,
G.Name
FROM
( SELECT G2.Day,
FirstPlace.FirstPlacePoints FirstPlace,
MAX( G2.Points ) as SecondPlace
FROM
Games G2,
( SELECT Day,
MAX( Points ) as FirstPlacePoints
FROM
Games
GROUP BY
Day ) FirstPlace
WHERE
G2.Day = FirstPlace.Day
AND G2.Points < FirstPlace.FirstPlacePoints
GROUP BY
1, 2 ) as FS,
Games G
WHERE
FS.Day = G.Day
and FS.FirstPlace = G.Points
ORDER BY
Margin desc
LIMIT 1