How to select win/loss record in mysql - mysql

I have table "games" that looks like this
| team1_id | team2_id | team1_pts | team2_pts |
1 3 101 117
2 5 99 98
I would like to do select that gives back team name from table "team" and win/loss record like this
| team_name | win | loss
Boston 15 11
Miami 13 12
I figured out how to get table with number of wins only but have no idea how to get wins and losses in same table.

You can unpivot and re-join:
select t.name, sum(is_win) as num_wins, sum(is_loss) as num_losses
from ((select (case when team1_pts > team2_pts then team1_id else team2_id end) as team_id,
1 as is_win, 0 as is_loss
from game g
) union all
(select (case when team1_pts < team2_pts then team1_id else team2_id end) as team_id,
0, 1
from game g
)
) g join
teams t
on t.team_id = g.team_id
group by t.name

I think you should revisit your data model. Maybe with GAMES table having fields GAME_ID, TEAM_ID, POINTS. You can probably get it working with what you have so far, but its not ideal.

This will do it:
select team_id ,
sum(case when Points > 0 then 1 else 0 end) as Win,
sum(case when Points < 0 then 1 else 0 end) as Loss
from
(
select team1_id as team_id , team1_pts - team2_pts as Points from games
union all
select team2_id, team2_pts - team1_pts as Points from games
)tbl
group by team_id

Related

MySQL Where Clause with Union All getting wrong results

I will preface this by saying I am still very much learning MySQL, and I am absolutely at that stage where I know just enough to be dangerous.
I have a database with data for scorekeeping for a sports league. We record wins/losses as either 1 or zero points. There is a night that has double play involved (meaning the players play twice in a single night, for 2 different formats). My data is structured like so (just a sample, I have hundreds of rows, over different formats):
ID
FID
WK
Type
HomeTeam
AwayTeam
HF1
HF2
AF1
AF2
1
44
1
PL
TM1
TM2
1
0
0
1
2
44
1
PL
TM3
TM4
0
0
1
1
3
44
2
PL
TM2
TM3
1
1
0
0
4
44
2
PL
TM4
TM1
0
1
1
0
5
44
3
PL
TM3
TM1
999
0
999
1
6
44
3
PL
Tm2
TM4
1
0
0
1
Where the 999 is used as a code number for us to know that the match hasn't yet been played, or the scoresheet hasn't been turned in to us for recordkeeping. (I use PHP to call these to a website for users to see what is going on, and am using an IF statement to convert that 999 to "TBD" on the website)
I can pull the Format 1 and Format 2 scores separately and get a listing just fine, but when I try to pull them together and get a total score, I am getting an incorrect count. I know the error lies with my WHERE Clause, but I've been banging my head trying to get it to work correctly, and I think I just need an extra set of eyes on this.
My current SQL Query is as follows:
SELECT Team,
SUM(TotalF1) AS TotalF1,
SUM(TotalF2) AS TotalF2,
SUM(TotalF1+TotalF2) AS Total
FROM ( ( SELECT HomeTeam AS Team,
HF1 AS TotalF1,
HF2 AS TotalF2
FROM tbl_teamscores
WHERE FID = 44
AND Type = 'PL'
AND HF1 != 999
AND HF2 != 999 )
UNION ALL
( SELECT AwayTeam,
AF1,
AF2
FROM tbl_teamscores
WHERE FID = 44
AND Type = 'PL'
AND AF1 != 999
AND AF2 != 999 )
) CC
GROUP BY Team
ORDER BY Total desc, Team ASC;
I am getting incorrect totals though, and I know the reason is because of those 999 designations, as the WHERE clause is skipping over ALL lines where either home or away score matches 999.
I tried separating it out to 4 separate Select Statements, and unioning them, but I just get an error when I do that. I also tried using Inner Join, but MySQL doesn't seem to like that either.
Edit to add DBFiddle with Real World Table Data and queries: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=1d4d090b08b8280e734218ba32db6d88
An example of the problem can be observed when looking at the data for Player 10. The overall total should be 13, but I am only getting 12.
Any suggestions would be very helpful.
Thanks in advance!
You can use conditional aggregation:
SELECT Team,
SUM(CASE WHEN Total8 <> 999 THEN Total8 END) AS Total8,
SUM(CASE WHEN TotalLO <> 999 THEN TotalLO END) AS TotalLO,
SUM(CASE WHEN Total8 <> 999 THEN Total8 END) + SUM(CASE WHEN TotalLO <> 999 THEN TotalLO END) AS Total
FROM (
SELECT HomeTeam AS Team, Home8PTS AS Total8, HomeLOPTS AS TotalLO FROM tbl_teamscores WHERE FID = 44 AND Type = 'PL'
UNION ALL
SELECT AwayTeam, Away8PTS, AwayLOPTS FROM tbl_teamscores WHERE FID = 44 AND Type = 'PL'
) CC
GROUP BY Team
ORDER BY Team ASC;
or:
SELECT Team,
SUM(NULLIF(Total8, 999)) AS Total8,
SUM(NULLIF(TotalLO, 999)) AS TotalLO,
SUM(NULLIF(Total8, 999)) + SUM(NULLIF(TotalLO, 999)) AS Total
FROM (
SELECT HomeTeam AS Team, Home8PTS AS Total8, HomeLOPTS AS TotalLO FROM tbl_teamscores WHERE FID = 44 AND Type = 'PL'
UNION ALL
SELECT AwayTeam, Away8PTS, AwayLOPTS FROM tbl_teamscores WHERE FID = 44 AND Type = 'PL'
) CC
GROUP BY Team
ORDER BY Team ASC;
If you get nulls in the results then you should also use COALESCE():
SELECT Team,
COALESCE(SUM(NULLIF(Total8, 999)), 0) AS Total8,
COALESCE(SUM(NULLIF(TotalLO, 999)), 0) AS TotalLO,
COALESCE(SUM(NULLIF(Total8, 999)), 0) + COALESCE(SUM(NULLIF(TotalLO, 999)), 0) AS Total
FROM (
SELECT HomeTeam AS Team, Home8PTS AS Total8, HomeLOPTS AS TotalLO FROM tbl_teamscores WHERE FID = 44 AND Type = 'PL'
UNION ALL
SELECT AwayTeam, Away8PTS, AwayLOPTS FROM tbl_teamscores WHERE FID = 44 AND Type = 'PL'
) CC
GROUP BY Team
ORDER BY Team ASC;
See the demo.

How to add a null value in the group_concat if the record does not exist?

I have this SQL query with substring_index and group_concat but the results I get does not give the right location of the values because the values does not exist.
I need to add a null or zero value in order to have the right location of the values in the sql result.
In the table there are three lid (1, 2, 3). The lid should be the basis count of the P's (P1, P2, P3) for the substring_index.
This is the table:
lid class_id class total
----- ------- ----- -----
1 73 Leader 10000
1 77 Consultant 8000
1 83 Coordinator 6000
2 73 Leader 20000
2 76 Staff 8000
2 77 Consultant 10000
3 73 Leader 30000
3 78 Team Leader 8000
This is the SQL query I used to group_concat for the totals and substring_index to separate the grouped values with their each column (P1, P2, P3)
SELECT *, GROUP_CONCAT(lid) as lids, GROUP_CONCAT(pyear) as pyears,
COUNT(DISTINCT lib_id) AS total_count,
CASE WHEN COUNT(*)>=1 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',1),' ',-1) END AS P1,
CASE WHEN COUNT(*)>=2 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',2),' ',-1) END AS P2,
CASE WHEN COUNT(*)>=3 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',3),' ',-1) END AS P3
FROM (
SELECT * FROM view_items WHERE lid='1'
UNION
SELECT * FROM view_items WHERE lid='2'
UNION
SELECT * FROM view_items WHERE lid='3'
) AS AZ GROUP BY class_id
This is the result of the above query:
class id class lids P1 P2 P3
--------- ----- ----- ---- ---- ----
73 Leader 1,2,3 10000 20000 30000
76 Staff 2 8000
77 Consultant 1,2 8000 10000
78 Team Leader 3 8000
83 Coordinator 1 6000
The lids should always have three count even though the record does not exists, a zero or null value should be added. How to do the adding of null value?
This is the expected result I need.
class id class lids P1 P2 P3
--------- ----- ----- ---- ---- ----
73 Leader 1,2,3 10000 20000 30000
76 Staff 0,2,0 0 8000 0
77 Consultant 1,2,0 8000 10000 0
78 Team Leader 0,0,3 0 0 8000
83 Coordinator 1,0,0 6000 0 0
To get 0 values where no lid is present in the table, you need to generate a list of all lid values for all class_id values, which you can do with a CROSS JOIN of two SELECT DISTINCT queries (one for lid, one for class_id). This can then be LEFT JOINed to the table, to get the required total value for each P group using conditional aggregation:
SELECT c.class_id,
MAX(v.class),
GROUP_CONCAT(COALESCE(v.lid, 0) ORDER BY l.lid) AS lids,
MAX(CASE WHEN v.lid=1 THEN total ELSE 0 END) AS P1,
MAX(CASE WHEN v.lid=2 THEN total ELSE 0 END) AS P2,
MAX(CASE WHEN v.lid=3 THEN total ELSE 0 END) AS P3
FROM (SELECT DISTINCT lid FROM view_items) l
CROSS JOIN (SELECT DISTINCT class_id FROM view_items) c
LEFT JOIN view_items v ON v.lid = l.lid AND v.class_id = c.class_id
GROUP BY c.class_id
Output:
class_id class lids P1 P2 P3
73 Leader 1,2,3 10000 20000 30000
76 Staff 0,2,0 0 8000 0
77 Consultant 1,2,0 8000 10000 0
78 Team Leader 0,0,3 0 0 8000
83 Coordinator 1,0,0 6000 0 0
Demo on dbfiddle
Use else 0 in case expression
SELECT *, GROUP_CONCAT(lid) as lids, GROUP_CONCAT(pyear) as pyears,
COUNT(DISTINCT lib_id) AS total_count,
CASE WHEN COUNT(*)>=1 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',1),' ',-1) else 0 END AS P1,
CASE WHEN COUNT(*)>=2 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',2),' ',-1) else 0 END AS P2,
CASE WHEN COUNT(*)>=3 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',3),' ',-1) else 0 END AS P3
FROM (
SELECT * FROM view_items WHERE lid='1'
UNION
SELECT * FROM view_items WHERE lid='2'
UNION
SELECT * FROM view_items WHERE lid='3'
) AS AZ GROUP BY class_id
I think the problem is, that you say count >= 1, but count >= 3 is also count >=1 so your code never reaches this line. You have to say,
CASE WHEN COUNT() >=1 AND COUNT () < 2...
CASE WHEN COUNT() >=2 AND COUNT () < 3...
I don't think GROUP_CONCAT() is the right approach for what you want. Try this:
SELECT vi.class_id, vi.class,
COUNT(DISTINCT vi.lib_id) AS total_count,
CONCAT_WS(',',
MAX(CASE WHEN vi.lid = 1 THEN 1 ELSE 0 END),
MAX(CASE WHEN vi.lid = 2 THEN 1 ELSE 0 END),
MAX(CASE WHEN vi.lid = 3 THEN 1 ELSE 0 END)
) as lids,
SUM(CASE WHEN vi.lid = 1 THEN vi.total ELSE 0 END) as total_1,
SUM(CASE WHEN vi.lid = 2 THEN vi.total ELSE 0 END) as total_2,
SUM(CASE WHEN vi.lid = 3 THEN vi.total ELSE 0 END) as total_3
FROM view_items vi
WHERE vi.lid IN (1, 2, 3)
GROUP BY vi.class_id;
Notes:
Your subquery and UNION are not needed. In MySQL, these can actually hurt performance.
I assume that lid is a number, so I've removed the single quotes.
You can use conditional aggregation for each of the totals that you want. Parsing GROUP_CONCAT() is not the right way to do this.
Your question is about the lids. The CONCAT_WS() does what you want -- concatenating either the value (if it appears) or zero if it does not.

Rank standings table of soccer matches by H2H

Let me start explaining this with an example, I have a table with records of matches played in a soccer league, by using this table and its matches results am able to generate a standings table for the teams in this league via a mysql query.
Table [matches] (example)
--------------------------------------------------------
|id | hometeam |goalsfor|goalsagainst| awayteam |
--------------------------------------------------------
--------------------------------------------------------
| 8 | Real Madrid | 2 | 0 | Inter Milan |
--------------------------------------------------------
| 9 | Inter Milan | 3 | 3 | Real Madrid |
--------------------------------------------------------
Generated standings by query
Pos Team Pld W D L F A GD Pts
1 FC Barcelona 5 2 3 0 8 5 3 9
2 Inter Milan 6 2 2 2 11 10 1 8
3 Real Madrid 6 2 2 2 8 8 0 8
4 AC Milan 5 0 3 2 8 12 -4 3
The query:
select
team,
count(*) played,
count(case when goalsfor > goalsagainst then 1 end) wins,
count(case when goalsagainst> goalsfor then 1 end) lost,
count(case when goalsfor = goalsagainst then 1 end) draws,
sum(goalsfor) goalsfor,
sum(goalsagainst) goalsagainst,
sum(goalsfor) - sum(goalsagainst) goal_diff,
sum(
case when goalsfor > goalsagainst then 3 else 0 end
+ case when goalsfor = goalsagainst then 1 else 0 end
) score
from (
select hometeam team, goalsfor, goalsagainst from scores
union all
select awayteam, goalsagainst, goalsfor from scores
) a
group by team
order by score desc, goal_diff desc;
What I want to do is to order the standings based on Head to Head matches, so it would first order by points, then if there's a draw in points the second sorting would be to look at the two teams matches and compare who has more wins, or scored more than the other, then use that to sort the table.
By doing this as in the example Real Madrid will become ranked as 2nd and then Inter Milan as 3rd.
How can I achieve this?
I want to compare the two teams matches when they are equal in points, and use that to sort.
ORDER BY score DESC, h2h DESC; goal_diff DESC
Update: I ended going with a solution mix of sql and php, first I find equaled teams in rank, and then generate mini h2h standings for those team and update the rank based on it. I still see this doable with just sql, but with my heavy query its too complicated to implement with just sql, thats why I mixed with php in the implementation.
You need to process this in two steps. First, run the query above and store the results in a work table (call it work below). Then you need to get a tie breaker score for each team that is on the same score. Below, I join the matches table to the work table for each team, and ignore any where the work rows do not have the same score, as they are not important. Then give the team 1 if they won. Have to do it again for the other side. You might want to change this to the 3 for win, 1 for draw.
Sum these results up, join that result to the team row in work, and you have a tie break score for each row where where the score is the same.
You need to check what happens if you have many teams on the same score, and see if this is the result you want.
select w.*, b.hth
From work w
left outer join (
select team, SUM(hth) hth
from (
Select hometeam team, case when m.goalsfor > m.goalsagainst then 1 else 0 end hth
from matches m
inner join work w1 on m.hometeam = w1.team
inner join work w2 on m.awayteam = w2.team
where w1.score = w2.score
union all
Select awayteam team, case when m.goalsAgainst > m.goalsFor then 1 else 0 end hth
from matches m
inner join work w1 on m.hometeam = w1.team
inner join work w2 on m.awayteam = w2.team
where w1.score = w2.score
) a --all hth at same points
group by team
) b --summed to one row per team
on b.team = w.team
order by w.score desc, b.hth desc;

How to achieve MySQL Query combining multiple COUNT() and GROUP BY

I have this data:
id date userid result
1 2015-05-01 1 a
2 2015-05-02 1 b
3 2015-05-03 1 b
4 2015-05-03 1 a
5 2015-05-04 1 a
I need to get users sorted by result:
id a b
1 1 1
2 1 1
You want conditional aggregation:
SELECT user_id, sum(result = 'win') AS wins, sum(result = 'loss') as losses
FROM table
GROUP BY user_id
ORDER BY wins DESC
LIMIT 4;
I would do it like this:
SELECT user_id,
SUM(CASE WHEN result='win' THEN 1 ELSE 0 END) AS wins,
SUM(CASE WHEN result='loss' THEN 1 ELSE 0 END) AS losses
FROM table1
GROUP BY user_id
ORDER BY wins DESC
LIMIT 4
Fiddle is here: http://sqlfiddle.com/#!9/00ac7/8

MySQL Joining two queries for sports team

I am trying to display a list of teams with the number of goals they have scored (and order them by greatest to smallest) but am having trouble with joining all the queries together
Table 1: Teams
teamid teamname
1 team1
2 team2
3 team3
Table 2: Results
id gameid teamid gf
1 1 1 5
2 2 1 3
3 1 2 0
4 2 2 2
5 3 3 0
What I'm trying to achieve
1. Team1 8
2. Team2 2
3. Team3 0
Get list of all teams
SELECT team.teamid, team.teamname
FROM teams team
Gets sum of goals for 1 team
SELECT COALESCE( SUM( gf ) , 0 ) goalsfor
FROM results
WHERE teamid = 1
Joining of queries
SELECT team.teamid,
team.teamname,
COALESCE(res.gf, 0) goalsfor
FROM teams team
LEFT JOIN
(SELECT COALESCE(SUM(res.gf), 0) goalsfor
FROM results res
GROUP BY teamid) res ON team.teamid = res.teamid
ORDER BY goalsfor DESC
Been stuck on joining the queries all day
SELECT teams.teamname, res.goals FROM teams JOIN (
SELECT COALESCE(SUM(results.gf),0) AS goals, results.teamid AS teamid FROM results
group by results.teamid) res
ON teams.teamid=res.teamid ORDER BY goals DESC;
My solution is:
SELECT t.id,
t.name,
SUM(r.gf) goalsfor
FROM team t
LEFT JOIN
results r ON t.id = r.teamId
GROUP BY t.id
ORDER BY goalsfor DESC
My result from my dummy table:
id name goalsfor
1 Apple 8
2 Banana 2
3 Carrot 0
I don't think you need COALESCE if you made your columns have a default of 0 and cannot be null.