SQL join tables and get Average - mysql

I asked yesterday a little bit similar question (I thought that that was my problem but later i realised that there was a fault). But that question got couple of nice answers and it did not make sense to change that question. And i think this question is enough different.
Question:
I have four tables and i need to calculate the Average points that each School has gotten.
Problem: the School Average should be calculated by the two latest Points each Team has gotten. At the moment the Query calculates all the points a Teams has gotten in the average.
A School can have multiple Teams and Teams can have multiple points. And from each team only the two latest points should be calculated in the School Average. Each School should also get the proper City KAID (CITY_ID). In the sqlFiddle everything works but the Average is wrong because it calculates all the points a Team has gotten.
I have created a simplificated working: sqlFiddle
The average for SCHOOL1 should be 2,66...
Example:
Let's say that Team10 has 6 points:
TEAM10 3..4..7..0..3..5 = 8 (3+5=8)
Only the latest two points should be calculated in the average (3 and 5). This should happen for all the teams.
I have tried couple of Queries but they don't work.
Query 1 (Problem: calculates all the points):
SELECT SNAME As School, AVG(PTS) As Points, ka.KAID As City_id FROM
Schools op
LEFT JOIN Points pi
ON op.OPID = pi.OPID
LEFT JOIN Citys ka
ON op.KAID = ka.KAID
GROUP BY SNAME, ka.KAID
ORDER BY City_id, Points, School ASC
Query 2 (Problem: Average wrong and duplicates):
SELECT IFNULL(AVG(PTS), 0) AS AVG, po2.KAID AS KID, SNAME AS SNAM FROM
(
SELECT te1.ID, te1.KAID, po1.PTS, te1.OPID FROM Points po1
INNER JOIN Teams te1 ON te1.ID = po1.TEID
GROUP BY po1.TEID, te1.ID HAVING count(*) >= 2
)
po2 INNER JOIN Schools sch1 ON po2.KAID = sch1.KAID
GROUP BY sch1.SNAME, sch1.OPID
ORDER BY po2.ID DESC
I am quite new to sql I have tried different Queries but i haven't gotten this to work properly.
If something is not clear please ask i will try to Explain it better.

try running this...
SELECT
SNAME As School,
SUM(pts)/ count(*) As Points,
ka.KAID As City_id
FROM Schools op
LEFT JOIN Points pi
ON op.OPID = pi.OPID
LEFT JOIN Citys ka
ON op.KAID = ka.KAID
GROUP BY SNAME, ka.KAID
ORDER BY City_id, Points, School ASC
DEMO
From what I see you have for the first school and the first city 8 rows with the sum = 29.
29/8 = 3.25.. you are joining the tables on the correct fields and the query is returning the rows in the table based on the opid and kaid so it seems the results are correct.. i'm guessing the avg function is not including the 0's or something but the results are there
EDIT:
to get it for the two newest rows you need to look at the greatest id per school and then the second greatest.. this will do what you want.
SELECT
SNAME As School,
SUM(pts)/ count(*) As Points,
ka.KAID As City_id
FROM Schools op
LEFT JOIN Points pi ON op.OPID = pi.OPID
LEFT JOIN Citys ka ON op.KAID = ka.KAID
JOIN
( ( SELECT MAX(id) as f_id
FROM points
GROUP BY TEID
ORDER BY f_id
)
UNION
( SELECT p1.id
FROM
( SELECT MAX(id) as t_id
FROM points
GROUP BY TEID
ORDER BY t_id
)t
LEFT JOIN points p1 on p1.id = (t.t_id -1)
)
) temp ON temp.f_id = pi.id
GROUP BY SNAME, ka.KAID
ORDER BY City_id, Points, School ASC;
ANOTHER DEMO

Related

How do add data to multiple columns at the same time

I got this last task before I can go to bed...
Make a query that shows the name(not the id) of players who have won the lottery more than once, how many times they've won and the name(not the id) of the municipality they live in.
Players-table: PlayerNum, Name, Address, MunicipalityID
Winners-table: PlayerNum, DrawID
Municipality-table: MunicipalityID, County, Population, Name
Thank you sooo much in advance!!
You need to join the tables and do a sub query on the winner table using count and group by the join the result set with player
Not sure what the draw table does
You really should make an attempt instead of just asking for the solution.
Your starting point is to find the users who have won more than once. This is a simple GROUP BY of PlayerNum and the HAVING clause to limit the result based on the COUNT -
SELECT PlayerNum, COUNT(DrawID) AS num_wins
FROM Winners
GROUP BY PlayerNum
HAVING num_wins > 1
The next step is to add the names of the players. For this you need to join to the Players table and I have added table aliases (w & p) to avoid retyping the full table name each time -
SELECT p.Name, COUNT(DrawID) AS num_wins
FROM Winners w
INNER JOIN Players p
ON w.PlayerNum = p.PlayerNum
GROUP BY w.PlayerNum
HAVING num_wins > 1
And then finally the join to Municipality to get the Name with a column alias as we already have a Name column -
SELECT p.Name, COUNT(DrawID) AS num_wins, m.Name AS MunName
FROM Winners w
INNER JOIN Players p
ON w.PlayerNum = p.PlayerNum
INNER JOIN Municipality m
ON p.MunicipalityID = m.MunicipalityID
GROUP BY w.PlayerNum
HAVING num_wins > 1

MySQL Query Never Returns

EDIT: This has been solved, requiring a subquery into the appearances table. Here is the working solution.
SELECT concat(m.nameFirst, ' ', m.nameLast) as Name,
m.playerID as playerID,
sum(b.HR) as HR
FROM Master AS m
INNER JOIN Batting AS b
ON m.playerID=b.playerID
WHERE ((m.weight/(m.height*m.height))*703) >= 27.99
AND m.playerID in (SELECT playerID FROM appearances GROUP BY playerID HAVING SUM(G_1b+G_dh)/SUM(G_All) >= .667)
GROUP BY playerID, Name
HAVING HR >= 100
ORDER BY HR desc;
I'm working with the Lahman baseball stat database, if anyone's familiar.
I'm trying to retrieve a list of all large, slugging first basemen, and the data I need is spread across three different tables. The way I'm doing this is finding players of a minimum BMI, who have spent at least 2/3 of their time at first/designated hitter, and have a minimum number of home runs.
'Master' houses player names, height, weight (for BMIs).
'Batting' houses HR.
'Appearances' houses games played at first, games played at DH, and total games.
All three databases are connected by the same 'playerID' value.
Here is my current query:
SELECT concat(m.nameFirst, ' ', m.nameLast) as Name,
m.playerID as playerID,
sum(b.HR) as HR
FROM Master AS m
INNER JOIN Batting AS b
ON m.playerID=b.playerID
INNER JOIN Appearances AS a
ON m.playerID=a.playerID
GROUP BY Name, playerID
HAVING ((m.weight/(m.height*m.height))*703) >= 27.99
AND ((SUM(IFNULL(a.G_1b,0)+IFNULL(a.G_dh,0)))/SUM(IFNULL(a.G_All,0))) >= .667
AND HR >= 200
ORDER BY HR desc;
This appears correct to me, but when entered it never returns (runs forever) - for some reason I think it has something to do with the inner join of the appearances table. I also feel like there's a problem with combining m.weight/m.height in a "HAVING" clause, but with aggregates involved I can't use "WHERE." What should I do?
Thanks for any help!
EDIT: After removing all conditionals, I'm still getting the same (endless) result. This is my simpler query:
SELECT concat(m.nameFirst, ' ', m.nameLast) as Name,
m.playerID as playerID,
sum(b.HR) as HR
FROM Master AS m
INNER JOIN Batting AS b
ON m.playerID=b.playerID
INNER JOIN Appearances AS a
ON m.playerID=a.playerID
GROUP BY playerID, Name
ORDER BY HR desc;
My guess is that the problem with your query is that each player has appeared many times (appearances) and at bat many times. Say a player has been at bat 1000 times in 100 games. Then the join -- as you have written it -- will have 100,000 rows just for that player.
This is just a guess because you have provided no sample data to verify if this is the problem.
The solution is to pre-aggregate the appearances and games tables as subqueries (at the playerId level) and then join them back.

MySQL: SUM Joined Table on Limited Selection of Rows

I have a problem similar to this question but a bit more complicated and I'm having trouble figuring out how to do it efficiently. Given two tables, one for a list of athletes and one with a list of races they've run in, e.g.,
ATHLETES
id
name
gender
details
RACES
athlete_id
year
points
I want to rank all of the athletes by gender for a given period of years using only their top 4 race finishes (where "top" is defined by points). I feel like I should be able to do this in a subquery but I can't figure out how to reference the outer query from the inner. What I have now looks like this:
SELECT SUM(points) as points, a.* FROM
(SELECT rr.points, inner_a.id as athlete_id
FROM athletes_raceresult rr
INNER JOIN athletes_athlete inner_a ON rr.athlete_id = inner_a.id
WHERE inner_a.gender ='m' AND rr.year BETWEEN 2012 AND 2014
AND inner_a.id = a.id
ORDER BY rr.points DESC) as races
INNER JOIN athletes_athlete a ON races.athlete_id = a.id
GROUP BY races.athlete_id
ORDER BY points DESC
But that doesn't limit the points to 4 rows per athlete. It looks like I want a correlated subquery, but I can't get that to work.
The following SQL Fiddle example illustrates how this can be done:
SELECT SUM(rr.points), a.id
FROM athletes_raceresult AS rr
INNER JOIN athletes_athlete AS a ON rr.athlete_id = a.id
WHERE (
SELECT count(crr.points)
FROM athletes_raceresult AS crr
INNER JOIN athletes_athlete AS ca ON crr.athlete_id = ca.id
WHERE ca.gender = 'm'
AND crr.year BETWEEN 2012 AND 2014
AND crr.athlete_id = a.id AND crr.points >= rr.points
) <= 4
AND a.gender = 'm'
AND rr.year BETWEEN 2012 AND 2014
GROUP BY a.id

Count rows with bigger average

Each student has some scores (and each score has student_id column).
I want to calculate student average, compare his average with other student, and find his position in his class.
Is it possible to find his position based on his average with 1 query? (may contains subqueries or joins)
I can sort all students by their average by this query:
SELECT s.*
FROM
scores s LEFT JOIN lessons lesson
ON lesson.id = s.lesson_id
WHERE lesson.display = 1
GROUP BY s.student_id
ORDER BY AVG(s.score) DESC
but it needs processing with PHP array_search function. (I think using MySQL functions is better, in this situation)
select student_id, AVG(scores) as 'average' from lessons as l, scores as s
where lessons.id = s.lesson_id and lesson.display = 1
GROUP BY s.student_id order by average desc`
Try this query
sample http://sqlfiddle.com/#!2/4fb8d/1
Hope this helps
select s.id, AVG(l.scores) as average from lessons as l, student as s
where l.id = s.lessonid and l.display = 1
GROUP BY s.id order by average desc
can solve your problem
Check this link
Thanks to Meherzad's sql query
I think you are looking for something like this:
SELECT COUNT(DISTINCT average)
FROM (
SELECT AVG(score) as average
FROM scores INNER JOIN lessons
ON scores.lesson_id = lessons.id
WHERE display=1
GROUP BY student_id
) sub_scores
WHERE average >= (SELECT AVG(score)
FROM scores INNER JOIN lessons
ON scores.lesson_id = lessons.id
WHERE display=1
AND student_id=1)
In the subquery sub_scores I'm calculating all averages of all students, in the outer query I'm calculating the number of distinct averages bigger than the average of student 1.
This will return the position of student 1.
Depending on what you are after, you might want to remove DISTINCT clause.
See this fiddle.
You need to use a user defined variable over the result set.
Try this:
SELECT *, (#rank := if(#rank is null, 1, #rank + 1)) as rank
FROM (SELECT s.*
FROM scores s
LEFT JOIN lessons lesson ON lesson.id = s.lesson_id
WHERE lesson.display = 1
GROUP BY s.student_id
ORDER BY AVG(s.score) DESC
) x

SQL - subqueries for top result without order by

(Sorry about the title, couldn't think of how to explain it)
So I have an Olympic database, the basic layout is that there's a competitors table with competitornum, givenname, and familyname (other columns aren't necessary for this) There's also a results table with competitornum, and place (between 1 and 8).
I'm trying to get the givenname and familyname and total number of gold, silver, and bronze medals (place = 1, 2 or 3)
It also needs to only display the results with the top number of medals, and all of this without using the Order By clause...
I asked this question before but realised I forgot to say some things, but the previous answer before the bold part was added was:
SELECT c.Givenname, c.Familyname, COUNT(r.places) AS TotalPlaces
FROM Competitors c INNER JOIN Results r
ON r.Competitornum = c.Competitornum
WHERE r.place IN (1,2,3)
GROUP BY c.Givenname, c.Familyname
I'm thinking it needs another subquery like
AND TotalPlaces = (SELECT MAX(TotalPlaces))
but I'm not sure how to use an alias in a subquery when it's above the subquery...
All help is appreciated, thanks!
EDIT: The official question on my assignment (I can't figure out the answer, I've really tried, that's why I'm here):
Which competitor(s) got the largest number of medals (counting gold, silver and bronze all together)? List their given and family names and the total number of their medals (only).
Warning: your solution must not assume that competitor names are always different
Do not use an ORDER BY clause, in any part of this query.
You need to have another subquery for this,
SELECT c.Givenname, c.Familyname, COUNT(r.places) AS TotalPlaces
FROM Competitors c
INNER JOIN Results r ON r.Competitornum = c.Competitornum
WHERE r.place IN (1,2,3)
GROUP BY c.Givenname, c.Familyname
HAVING COUNT(r.places) =
(
SELECT MAX(TotalPlaces)
FROM
(
SELECT COUNT(g.places) AS TotalPlaces
FROM Competitors f
INNER JOIN Results g ON f.Competitornum = g.Competitornum
WHERE g.place IN (1,2,3)
GROUP BY f.Givenname, f.Familyname
)
)
The final answer (thanks to John Woo and lc.)
Pasted this here for anyone that comes across this question in the future:
SELECT c.Givenname, c.Familyname, COUNT(r.place) AS TotalPlaces
FROM Competitors c
INNER JOIN Results r ON r.Competitornum = c.Competitornum
WHERE r.place IN (1,2,3)
GROUP BY c.competitornum, c.Givenname, c.Familyname
HAVING COUNT(r.place) =
(
SELECT MAX(TotalPlaces)
FROM
(
SELECT COUNT(r.place) AS TotalPlaces
FROM Competitors c
INNER JOIN Results r ON r.Competitornum = c.Competitornum
WHERE r.place IN (1,2,3)
GROUP BY c.competitornum, c.Givenname, c.Familyname
)
)