Multi-event tournament standings - mysql

I am trying to grab a participants rankings in a multi-event tournament.
I can do a ranking for a single event pretty easily. Is there a way to find ALL in one go?
Given input: "Bob"
Data example: Desired output:
Name | Event | Score Name | Event | Score | Rank
-------------------- ----------------------------
Bob 1 100 Bob 1 100 1
Bob 2 75 Bob 2 75 3
Bob 3 80 Bob 3 80 2
Jill 2 90
Jill 3 60
Chris 1 70
Chris 2 50
Chris 3 100
Amy 1 85
Amy 2 95
Amy 3 65
The catch: I do not have access to the Rank()
function with my version of SQL, and updating is not possible in this scenario.
Clearly I could just do the score per event separately in a loop,
but I'd like to try to do it all in one go.

You can emulate a ranking function in MySQL using a self-join to values with a higher score in the same Event, and then counting the number of higher scores for each participant:
SELECT s1.Name, s1.Event, s1.Score, COUNT(s2.Name)+1 AS Rank
FROM scores s1
LEFT JOIN scores s2 ON s2.Event = s1.Event AND s2.Score > s1.Score
WHERE s1.Name = 'Bob'
GROUP BY s1.Name, s1.Event, s1.Score
ORDER BY s1.Name, s1.Event
Output:
Name Event Score Rank
Bob 1 100 1
Bob 2 75 3
Bob 3 80 2
Demo on dbfiddle

Related

cluster number by range in sql

I have a data look like the following:
Mike 5
Mike 100
Mike 101
Mike 106
Mike 95
Mike 1000
Mike 1001
Mike 1010
Jen 2006
Jen 2001
Jen 2010
Jen 3000
Jen 10
I want to cluster the numbers by absolute value of 20, and leave the smallest one in each cluster.
The result looks like this:
Mike 5
Mike 95
Mike 1000
Jen 2006
Jen 3000
Jen 10
Is there any way to do this?
I have thought about GROUP BY with intervals,
but it does not make sense if the cluster cross the intervals,
for an example, if I set the ranges are
1-20, 21-40, 41-60
but if my data have:
Mike 35
Mike 39
Mike 41
Mike 45
it will be split into two clusters
Mike 35
Mike 41
what I want:
Mike 35
Thanks!
If I understand correctly, you want the "smallest" value for each name to start a "cluster". That cluster in turn contains all rows for the same name within a value of 20. This is then repeated for the remaining clusters.
This suggests a recursive CTE:
with recursive tn as (
select t.*, row_number() over (partition by name order by val) as seqnum
from t
),
cte as (
select name, val, seqnum, val as cluster_val, 1 as cluster_num
from tn
where seqnum = 1
union all
select cte.name, tn.val, tn.seqnum,
(case when tn.val < cte.cluster_val + 20 then cte.cluster_val else tn.val end) as cluster_val,
(case when tn.val < cte.cluster_val + 20 then cte.cluster_num + 1 else 1 end) as cluster_num
from cte join
tn
on tn.name = cte.name and tn.seqnum = cte.seqnum + 1
)
select *
from cte
where cluster_num = 1
order by name, val;
Here is a db<>fiddle.

Use avg() and joins mysql

Say I have a table called students
idStudent Name
1 Billy
2 Mariah
3 Chris
4 Mark
5 Sarah
and another table called tests
idTest score student_idstudent
1 50 1
2 100 1
3 90 2
4 100 3
5 45 4
is it possible to use a combination of a join and avg() to get a result like
idStudent avg_test
1 75
2 90
3 100
4 45
5 0
SELECT s.idStudent,
AVG(COALESCE(t.score, 0)) AS avg_test
FROM students s
LEFT JOIN tests t
ON s.idStudent = t.student_idStudent
GROUP BY s.idStudent

Most recent distinct record from a joined MySQL table

I have two tables, one of a list of competition results, and one with ELO ratings (based on previous competitions).
Fetching the list of competitors for an arbitrary competition is trivial enough, but I also need to get the most recent rating value for them.
score:
id | eventid | competitorid | position
1 1 1 1
2 1 2 2
3 1 3 3
4 2 2 1
5 2 3 2
6 3 1 1
7 3 3 2
8 3 2 3
rating:
id | competitorid | rating
1 1 1600
2 2 1500
3 3 1500
4 2 1600
5 3 1590
Expected output for a query against score.eventid = 3 would be
id | competitorid | position | rating
6 1 1 1600
7 3 2 1590
8 2 3 1600
At the moment my code looks like:
SELECT score.scoreID, score.competitorID, score.position,
rating.id, rating.rating
FROM score, rating
WHERE score.competitorid = rating.competitorid
AND score.eventid = 3
ORDER BY score.position
which gives an output of
id | competitorid | position | rating.id | rating
6 1 1 1 1600
7 3 2 2 1500
7 3 2 4 1590
8 2 3 3 1500
8 2 3 5 1600
basically it's showing the data from the score table for that correct event, but giving me a row for every rating available against that competitorID unfortunately I have no idea where to build in the DISTINCT statement or how to limit it to the most recent result.
MySQL noob, and managed DISTINCT statements, but not with joins. Unfortunately most previous questions seemed to deal with getting distinct results from a single table, which is not quite what I'm after. Thanks!
One way to get the rating is with a correlated subquery:
SELECT s.scoreID, s.eventID, s.competitorID, s.position,
(select r.rating
from rating r
where s.competitorID = r.competitorID
order by r.id desc
limit 1
) as rating
FROM score s
WHERE s.eventID = 3
ORDER BY s.position;
I'm not sure what ratingprid is, so this only includes the rating.

Can I use one query to count two columns in SQL

I have
office
office_id name
--------- -----------------
1 office1
2 office2
3 office3
person
uid office_id age gender
---------------------------
1 1 20 male
2 1 20 female
3 1 20 male
4 1 21 male
5 2 20 male
6 3 20 male
Is it possible I can use ONE query to get
office_id name age_20 male
-----------------------------
1 office1 3 3
2 office2 1 1
3 office3 1 1
Yes, you can. MySQL support boolean arithmethic and I think this is the shortest way to do it. If you want a more RDBMS friendly, use CASE WHEN age = 20 THEN 1 ELSE 0 END.
SELECT a.office_ID,
a.name,
SUM(age = 20) age_20,
SUM(gender = 'Male') male
FROM office a
LEFT JOIN person b
ON a.Office_ID = b.office_ID
GROUP BY a.office_ID, a.name
SQLFiddle Demo

mysql query but two differant group by

If my Data is
Name - playerID - matchID - Innings - Runs
James 1 1 1 5
James 1 1 2 8
Darren 2 1 1 3
Darren 2 1 2 9
James 1 2 1 10
James 1 2 2 12
Darren 2 2 1 13
Darren 2 2 2 19
and my sql data is
$query = "SELECT playerID, name,
SUM(runs) AS runs_scored,
MAX(runs) AS highest_score
FROM matchPlayer GROUP BY playerID";
Then the output would read
James has scored 35 runs with a highest score of 18
Darren has scored 44 runs with a highest score of 19
Now I wish to get the highest total scored in one match (that is combining innings 1 & 2)?
I have no idea how to start on this query :(
EDIT
The exact info I require is the HIGHEST match total, so James has 13 combined runs from matchID 1 and 22 combined runs from matchID 2 - so the answer I am after is 22.
You need to do it in two stages:
SELECT ms.playerID, mp.name, SUM(ms.runs_by_match) AS runs_scored,
MAX(ms.runs_by_match) as highest_score
FROM
matchPlayer as mp
INNER JOIN (
SELECT playerID, matchID, SUM(runs) AS runs_by_match
FROM matchPlayer
GROUP BY playerID, matchID
) AS ms ON mp.playerID = ms.playerID
GROUP BY
ms.playerID, mp.name