Query Wins and Losses With Score - mysql

If I have a table:
matches
id
team_one
team_two
one_score
two_score
How would I make a query giving me the following result:
Team A
Win: 2 Lose: 0 Score: 5
Team B
Win: 2 Lose: 0 Score: 3
Team C
Win: 1 Lose: 1 Score: 3
Team D
Win: 0 Lose: 2 Score: 1
The Team who have won has the higher score in the table.

The trick is that a given team may appear in one of two different columns. You can solve this with a UNION, where the second table for the union simply swaps the columns from the first table. After that it's just a simple GROUP BY:
SELECT team, SUM(Win) As Won, SUM(Loss) as Lost, SUM(score) as Score
FROM
( SELECT team_one as team,
CASE WHEN one_score > two_score THEN 1 ELSE 0 END as Win,
CASE WHEN one_score < two_score THEN 1 ELSE 0 END as Loss, one_score as score
FROM matches
UNION ALL
SELECT team_two as team
CASE WHEN two_score > one_score THEN 1 ELSE 0 END as Win,
CASE WHEN two_score < one_score THEN 1 ELSE 0 END as Loss, two_score as score
FROM matches
) t
GROUP BY team
ORDER By Won, Lost DESC, Score
As an aside, I worked on a database system like this a long time ago, that had a table with paired records. This was not for competition results, but it was paired data. We found it a lot more performant to store two records for each "match". The two records for a match would share a common match_id, but reverse the order in which the "teams" were listed. Front end code and a backend maintenance task were used to guarantee the paired records were stored properly.
This made certain queries we needed to do much more efficient, as we could index the table once and select from the table for specific teams just by looking at its equivalent team_one column. Again, this wasn't sports data, so your results may vary, but we did find for our problem space it worked much better.
Part of what made this efficient in the old system was that, once entered, the paired data rarely changed. This seems like a good fit for what you're doing.

Related

Creating a SQL view from tables without UIDs

I have two tables:
match_rating, which have data on a team's performance in a match. There are naturally two tuples for every matchId (since there are two teams to each match). The PK is matchId, teamId.
event, which has information on events during matches. The PK is an autoincremented UID, and it contains the Foreign Keys match_id and subject_team_id as well.
Now I want to create a new view which counts how many times certain events happen in a match, for each team, with fields like this:
But for the life of me I cannot get around the fact that there are 1) two tuples for each match in the match_rating table, and 2) querying the event table on match_id returns events for both teams.
The closest I got was something like this:
SELECT SUM(
CASE
WHEN evt.event_type_id = 101 THEN 1
WHEN evt.event_type_id = 111 THEN 1
WHEN evt.event_type_id = 121 THEN 1
[etc]
END
) AS 'mid_chances',
SUM(
CASE
WHEN evt.event_type_id = 103 THEN 1
WHEN evt.event_type_id = 113 THEN 1
WHEN evt.event_type_id = 123 THEN 1
[etc]
END
) AS 'right_chances',
mr.tactic,
mr.tactic_skill,
mr.bp,
evt.match_id,
evt.subject_team_id
FROM event evt
JOIN match_rating mr
ON evt.match_id = mr.match_id
WHERE evt.event_type_id BETWEEN 100 AND 104 OR
evt.event_type_id BETWEEN 110 AND 114 OR
evt.event_type_id BETWEEN 120 AND 124 OR
[etc]
GROUP BY evt.match_id
ORDER BY `right_chances` DESC
But still, this counts the events twice, reporting 2 events where there was only 1, 6 for 3 events and so on. I have tried grouping on team_id as well (GROUP BY evt.match_id AND team_id) , but that returns only 2 rows with all events counted.
I hope I have made my problem clear, and it should be obvious that I really need a good tip or two.
Edit for clarity (sorry):
Sample data for match_rating table:
Sample data for the event table:
What I would like to see as the result is this:
That is, two tuples for each match, one for each team, where the types of events that team had is summed up. Thanks so much for looking into this!
Update after comments/feedback
OK.. just to confirm, what you want is
Each row of the output represents a team within a match
Other values (other than match_id and team_id) are sums or other aggregations across multiple rows?
If that is the case, then I believe you should be doing a GROUP BY the match_id and team_id. This should cause the correct number of rows to be generated (one for each match_id/team_id combination). You say in your question that you have tried it already - I suggest reviewing it (potentially after also considering the below).
With your data, it appears that the 'event' table also has a field which indicates the team_id. To ensure you only get the relevant team's events, I suggest your join between match_rating and event be on both fields e.g.,
FROM event evt
JOIN match_rating mr
ON evt.match_id = mr.match_id
AND evt.subject_team_id = mr.team_id
Previous answer - does not answer the question (as per later comments)
Just confirming - the issue is that when you run it, for each match it returns 2 rows - one for each team - but you want to do processing on both teams as one row only?
As such, you could do a few things (e.g., self-join the match rating table to itself, with Team1 ratings and Team2 ratings).
Alternatively, you could modify your FROM to have joins to match_rating twice - where the first has the lower ID for the two teams e.g.,
FROM event evt
JOIN match_rating mr_team1
ON evt.match_id = mr_team1.match_id
JOIN match_rating mr_team2
ON evt.match_id = mr_team2.match_id
AND mr_team1.match_id < mr_team2.match_id
Of course, your processing then needs to be modified to take this into account e.g., one row represents a match, and you have a bunch of data for team1 and similar data for team2. You'd then, I assume, compare the data for team1 columns and team2 columns to get some sort of rating etc (e.g., chance for Team1 to win, etc).

Mysql SUM CASE with unique IDs only

Easiest explained through an example.
A father has children who win races.
How many of a fathers offspring have won a race and how many races in total have a fathers offspring won. (winners and wins)
I can easily figure out the total amount of wins but sometimes a child wins more than one race so to figure out winners I need only sum if the child has won, not all the times it has won.
In the below extract from a query I cannot use Distinct, so this doesn't work
SUM(CASE WHEN r.finish = '1' AND DISTINCT h.runnersid THEN 1 ELSE 0 END ) AS winners,
This also won't work
SUM(SELECT DISTINCT r.runnersid FROM runs r WHERE r.finish='1') AS winners
This works when I need to find the total amount of wins.
SUM(CASE WHEN r.finish = '1' THEN 1 ELSE 0 END ) AS wins,
Here is a sqlfiddle http://sqlfiddle.com/#!2/e9a81/1
Let's take this step by step.
You have two pieces of information you are looking for: Who has won a race, and how many races have they one.
Taking the first one, you can select a distinct runnersid where they have a first place finish:
SELECT DISTINCT runnersid
FROM runs
WHERE finish = 1;
For the second one, you can select every runnersid where they have a first place finish, count the number of rows returned, and group by runnersid to get the total wins for each:
SELECT runnersid, COUNT(*) AS numWins
FROM runs
WHERE finish = 1
GROUP BY runnersid;
The second one actually has everything you want. You don't need to do anything with that first query, but I used it to help demonstrate the thought process I take when trying to accomplish a task like this.
Here is the SQL Fiddle example.
EDIT
As you've seen, you don't really need the SUM here. Because finish represents a place in the race, you don't want to SUM that value, but you want to COUNT the number of wins.
EDIT2
An additional edit based on OPs requirements. The above does not match what OP needs, but I left this in as a reference to any future readers. What OP really needs, as I understand it now, is the number of children each father has that has run a race. I will again explain my thought process step by step.
First I wrote a simple query that pulls all of the winning father-son pairs. I was able to use GROUP BY to get the distinct winning pairs:
SELECT father, name
FROM runs
WHERE finish = 1
GROUP BY father, name;
Once I had done that, I used it is a subquery and the COUNT(*) function to get the number of winners for each father (this means I have to group by father):
SELECT father, COUNT(*) AS numWinningChildren
FROM(SELECT father, name
FROM runs
WHERE finish = 1
GROUP BY father, name) t
GROUP BY father;
If you just need the fathers with winning children, you are done. If you want to see all fathers, I would write one query to select all fathers, join it with our result set above, and replace any values where numWinningChildren is null, with 0.
I'll leave that part to you to challenge yourself a bit. Also because SQL Fiddle is down at the moment and I can't test what I was thinking, but I was able to test those above with success.
I think you want the father name along with the count of the wins by his sons.
select father, count(distinct(id)) wins
from runs where father = 'jack' and finish = 1
group by father
sqlfiddle
I am not sure if this is what you are looking for
Select user_id, sum(case when finish='1' then 1 else 0 end) as total
From table
Group by user_id

Getting scores from MySQL - better option than sub-queries?

I'm building a website for a friend (kind of a hobby thing, not for anything pro/work related) that'll store information about players, games and scores. I have built most of the reporting/statistical info but I want to display how many times a player hit the max score and am wondering if I can improve my idea (based on sub-queries). My 'scores' table is set out as so:
scores (id, gameID, playerID, venueID, versusID, score1, score2, score3, score4, score5, total, seasonID) - all the xID's are foreign keys.
The premise is that a new entry is made per game, per player so I have PHP insert data from text fields etc. This means that say there's 20 games in a season and for score1 'John Smith' hits the max score of 10 4 times that season. But he also hits it 8 times on score2, 6 times on score3 etc (and obviously, these could be in different games). So at the end of the season, I have a big table with a load of results in (I'd have 240 rows given there's 12 players per team) and when I'm looking at my stats, I want to find out how many times John Smith hit a 10 that season. I can obviously do 5 queries (or 1 with sub-queries) and add the results to tell me this, but I'm wondering what's the best method (or the one the 'SQL guru' would use, if you like) purely for my own development.
So to finish: I'm hoping to run my query and get a resultset that tells me:
Name | Total
John Smith | 12
Rob Smith | 11
Will Smith | 11
etc... | 1
The firstName and secondName are stored in the 'player' table (which is linked to the 'scores' table by the playerID foreign key). I'd like to be able to modify the query later on-demand if I wish, for example if I wanted to see how many times players scored a 9 rather than a 10 (but that can obviously be done by passing the number via PHP).
Searching here (+ Google) has lead me down the 'JOIN' route but I've not had much success. Any help, please? :)
I think this should do the trick:
SELECT playerID, COUNT(playerID) AS Total FROM (
SELECT playerID FROM scores WHERE score1='10'
UNION ALL
SELECT playerID FROM scores WHERE score2='10'
UNION ALL
SELECT playerID FROM scores WHERE score3='10'
UNION ALL
SELECT playerID FROM scores WHERE score4='10'
UNION ALL
SELECT playerID FROM scores WHERE score5='10'
) AS thetable
GROUP BY playerID
Where 10 is the score you want.
This will get the playerID with respective number of 10 scores:
select
playerID,
count(score1 = 10 or null) +
count(score2 = 10 or null) +
count(score3 = 10 or null) +
count(score4 = 10 or null) +
count(score5 = 10 or null)
as total
from scores
group by playerID
having total > 0
Join it to the player table to get the names.

Triple count and groupby using MySql on a simple table... is it possible in one query?

I'm having a question that can be explained using a simple fictive table.
Table "Drinks" has just three fields:
Id (1..N) - Primary key
Date ('2012-09-19'...) - Each date can occur very often
Hot (1 for yes, and 0 for false).
I would like to produce a list like this:
Date Total Hot Cold
2012-09-19 14 6 8
2012-09-10 21 18 3
Etc.
The field "Cold" is as you might expect calculated as (Total - Hot).
What I've got so far is:
SELECT Date, count(*) AS Total FROM Drinks GROUP BY Date;
This gives me the desired table, but of course without the columns "Hot" and "Cold".
Is there a way to modify my query so I can produce this table in one go? I can of course built the table in phases using PHP code, but that is probably not the elegant way nor the fastest.
I'm happy to watch and learn... :)
You can add CASE statements in your SELECT clause.
SELECT Date,
count(*) AS Total,
SUM(CASE WHEN Hot = 1 THEN 1 ELSE 0 END) totlHOT,
SUM(CASE WHEN Hot = 0 THEN 1 ELSE 0 END) totalCold
FROM Drinks
GROUP BY Date;
SELECT Date,
count(*) AS Total,
SUM(Hot = 1) Hot,
SUM(Hot = 0) Cold
FROM Drinks
GROUP BY Date;

table design for tracking head-to-head match records?

I am having trouble coming up with a workable table design for tracking head-to-head match results between users.
All users have a UID. Individual match results are stored in another table, and each match has its own UID.
What I want is to be able to pull something like this with a simple select:
Player vs. Opponent Wins Losses Draws
Bob John 5 2 1
Bob Sam 0 3 2
John Bob 2 5 1
I can pull this data out of the raw match results with some manipulation in PHP, but it's rather costly so I'd like to use cron jobs and store these "finished" statistics in a table for quick reads.
What's tripping me up is the fact that one data set (2 players, win, loss, draw counts) can be read in two directions, depending on which player's point of view you want, as depicted above for Bob and John.
I could make a table like this:
[player] [opponent] [wins] [losses] [draws]
but then each "set" would require two rows...
[player] [opponent] [wins] [losses] [draws]
bob john 5 2 1
john bob 2 5 1
and that duplication seems like it might cause me problems later, though off the top of my head I can't think of a reason why, just DRY and all that...
Suggestions?
I've seen the duplication approach you mention used effectively. So long as you're aware of the duplication, it's not too difficult to handle.
If you want to avoid the duplication, you'll probably need more code, and more code complexity: to show all the results for a single player, you'd need to find the records where that player is either "Player" or "Opponent".
One (I'd argue) useful way to report all the players' records (versus their opponents) would be to display each "versus" records twice -- once as a Player, grouped together, and once as an Opponent, in each of the sets of records for their opponents:
Player vs. Opponent Wins Losses Draws
Bob John 5 2 1
Bob Sam 0 3 2
John Bob 2 5 1
John Sam 1 2 1
Sam Bob 3 0 2
Sam John 2 1 1
Assuming you have a table (MatchID, Player1ID, Player2ID, Player1Score, Player2Score ...), with one row for each match (i.e. no duplication)
You can get all matches for a player via:
SELECT * from Matches WHERE Player1ID = #ID OR Player2ID = #ID
Possibly slightly more useful, though, is to rearrange the data slightly so it's always player and opponent:
SELECT MatchID, Player1ID, Player2ID, Player1Score, Player2Score ... FROM Matches where Player1ID = #ID
UNION
SELECT MatchID, Player2ID, Player1ID, Player2Score, Player1Score ... FROM Matches where Player2ID = #ID
(note that both the ID and the score order is reversed in the second union statement)
You can also generalise this for stats building:
SELECT stats.Player, stats.Opponent, SUM(stats.PlayerWin) AS Wins, SUM(stats.Draw) AS Draws, SUM(stats.OpponentWin) AS Losses
FROM (
SELECT Player1ID AS Player, Player2ID AS Opponent,
CASE WHEN Player1Score > Player2Score THEN 1 ELSE 0 END AS PlayerWin,
CASE WHEN Player1Score = Player2Score THEN 1 ELSE 0 END AS Draw,
CASE WHEN Player2Score > Player1Score THEN 1 ELSE 0 END AS OpponentWin
UNION
SELECT Player2ID AS Player, Player1ID AS Opponent,
CASE WHEN Player2Score > Player1Score THEN 1 ELSE 0 END AS PlayerWin,
CASE WHEN Player2Score = Player1Score THEN 1 ELSE 0 END AS Draw,
CASE WHEN Player1Score > Player2Score THEN 1 ELSE 0 END AS OpponentWin
) stats
GROUP BY stats.Player, stats.Opponent
I guess my general point is you can have it either way, they're roughly equally complicated (if you put data into the database twice, you have to make sure you keep it in sync when you update it. If you put data into the database once, you have to count it twice when you're doing stats. Also, if you put data into the database twice, that complicates things a bit from the database side since MatchID won't be unique any more.)