Mysql show count on join even if count is 0 - mysql

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.

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

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)

Calculate sum of last n games Mysql

I keep track of all european football leagues. But now I want a table with all of the teams and next to it there total goals scored and totals goals against of their last 5 games.
My results table has the following columns:Match_id | Date | home team| Away team | Home Goals | Away goals
So what would the code be for this issue as you have to check both the home team column and away team column to find their last 5 match results??
Pls HELP :D
Getting all goals for a team when it's a home/away team in the last 5 games would be:
SELECT home_team as team, sum(home_goals) as goals FROM tbl a WHERE match_id IN (SELECT match_id FROM tbl b WHERE b.home_team = a.home_team ORDER BY date desc limit 5) GROUP BY home_team
Similarly you can get the away team (lets call this query Q_home and the equivalent for the away team Q_away).
So overall you'd want to do something like:
SELECT team, a.goals+b.goals FROM <Q_home> a JOIN <Q_away> b ON a.team=b.team
Not really sure if this would work but this is the general idea.

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...

Group by two columns

The database table contains goal actions.
type goal assist
goal Johnny James
goal Johnny James
goal James Bob
When using GROUP BY goal, assist it displays
player goals assists
Johnny 2 0
Johnny 0 0
James 1 0
James 0 2
Bob 0 0
Bob 0 1
but I need it to show players' goals and assists in one line. Cheers.
You can do it like this (although that might not be the fastest query, depending on the size of your database and indexes!):
SELECT players.player,
-- Use correlated subselects to count goals / assists
(SELECT COUNT(*) FROM actions WHERE goal = players.player) goals
(SELECT COUNT(*) FROM actions WHERE assist = players.player) assists
-- Get all distinct players (UNION = DISTINCT, here). Of course, if you
-- have an actual players table, use that one instead!
FROM (
SELECT goal player FROM actions UNION
SELECT assist FROM actions
) players
From your question, I'm not sure if type = goal is relevant for your query...
A possible solution would be to unpivot the player names first, then group with pivoting:
SELECT
Player,
COUNT(NULLIF(ScoreType, 'assist')) AS goals,
COUNT(NULLIF(ScoreType, 'goal')) AS assists
FROM (
SELECT
CASE s.ScoreType WHEN 'goal' THEN goal ELSE assist END AS Player,
s.ScoreType
FROM GoalActions
CROSS JOIN (
SELECT 'goal' AS ScoreType
UNION ALL SELECT 'assist'
) st
) s
GROUP BY
Player
Unpivoting is done using a cross join to a virtual table, and grouping/pivoting is implemented in the form of aggregating with CASE.