MySQL Query Never Returns - mysql

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.

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

SUM before JOIN in MySQL?

I'm having a probably basic problem with an SQL query (I'm learning).
I'm tracking the vaccination status in the different Spanish regions.
Simplifying, I have two tables: one with the regions (ca) and their population, and the other with the region (ca), the day of each data (time) and the dosis administered.
In order to get the percentages of overall Spanish population vaccinated each day, I need to SUM all the populations of each region, and then divide the SUM of doses administered in all regions and then divide between that SUM.
However, when I do the JOIN, each population is added to every row, so the SUM is very high (it is counted once per time the region appears).
I think I need to SUM all the population before JOIN, but then, what column do I use to JOIN?
It is something like this:
SELECT
time AS "time",
SUM(SUM(v1.dose)) OVER (ORDER BY time)/'SP_population'
FROM vaccines v1
INNER JOIN
(SELECT SUM(population) AS 'SP_population' FROM ca_population) v2 ON ?????
GROUP BY time
ORDER BY time```
What should the ??? be?
If I understand correctly, you want a CROSS JOIN. You no longer care about regions so you want one population value for all rows:
SELECT v.time, p.sp_population, v.daily_dose,
SUM(SUM(v.daily_dose)) OVER (ORDER BY time) / p.sp_population
FROM (SELECT v.time, SUM(v.dose) as daily_dose
FROM vaccines v
GROUP BY v.time
) v CROSS JOIN
(SELECT SUM(population) as sp_population
FROM ca_population
) p
ORDER BY time

Good alternatives of comporting two subquery in the 'Where' clause

I have this schema:
CLUB(Name, Address, City)
TEAM(TeamName, club)
PLAYER(Badge, teamName)
MATCH(matchNumber, player1, player2, club, winner)
I need to make this query:
For each club, find the number of players in that club that have won
at least two games.
I wrote this:
SELECT teamName
From TEAM t join Match m1 on t.club=m1.club
WHERE Q2 >= ALL Q1
Q1:
SELECT Count (Distinct winner)
FROM MATCH
WHERE match m join player p on m. winner=player.badge
GROUP BY teamName
Q2:
SELECT Count (distinct winner)
FROM match m2
WHERE m2.club=m1.club
I don’t know if it is correct, however I heard that using this form where I confront two counts is not the best. Why?
Try something like this:
SELECT club, COUNT(*) as PlayerCount
FROM (SELECT club, winner
FROM match
GROUP BY club, winner
HAVING COUNT(*) > 1) a
GROUP BY club
The inner query should limit results to club/player combinations that have 2 or more wins, and the outer query will count the number of these players per club.
I don’t know if it is correct, however I heard that using this form where I confront two counts is not the best. Why?
Comparing two count subqueries is fine if you need to, but a good rule of thumb is to hit each table as few times as possible. Using multiple subqueries will end up hitting each table multiple times, and will usually result in longer execution times.
Try this query
SELECT t.club, COUNT(*)
FROM TEAM t
JOIN PLAYER p ON p.teamName = t.TeamName
JOIN (
-- Won at least 2 matches.
SELECT club, winner, COUNT(*) AS TheCount
FROM MATCH
GROUP BY club, winner
HAVING COUNT(*) > 1
) w ON w.winner = p.badge AND w.club = t.club
GROUP BY t.club

SQL join tables and get Average

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

MYSQL View and summing fields

I need some help I have been scouring the web and haven't been able to find something too similar. I have a MYSQL database for my Golf League. I need to display standings by creating a view from this database. There are 2 people per team, my primary key in 'players' is 'id' there is also a teamID (numerical value 1 - 20, 20 teams) for each player which corresponds to their teammates. Basically what I need is a view that contains 'teamID', both players 'LName' (maybe an 'LNameA','LNameB'), and a sum of the two players 'points' field. I have never summed a field from one person and another or created a view in MYSQL.
EDIT:
I was trying something like
CREATE
VIEW standings1
AS SELECT teamID, LName, points
FROM players
but need teamID to be the primaryKey of the view which will contain each players last name, and their points summed together.
Try this:
create view standings as
select teamId, group_concat(lname separator ', ') as TeamMembers,
sum(points) TotalPoints from players
group by teamId
Oh, one more thing. If you want to have the names of the players in different fields (group_concat just separate them by commas, but it is still a single field) you can use this query:
create view standings as
select a.teamId, a.lname as player1, b.lname as player2,
a.points + b.points TotalPoints
from players a
join players b ON a.teamId = b.teamId AND a.id >= b.id
group by a.teamId, a.id
having count(*) = 2
That way you can play better with the names in PHP without having to parse the ", "
If I understand your table structure, you will need a JOIN against the table's own teamID. I'm assuming the teamID refers to a team, and is not the id of the player. The trick here is to join two copies of the table on the same teamID, but where the player ids are non-equal. That should produce the pair of players per team.
CREATE VIEW standings AS
(
SELECT
p1.teamID AS teamID,
p1.id AS p1id,
p2.id AS p2id,
p1.LName AS p1LName,
p2.LName AS p2LName,
p1.score + p2.score AS totalScore
FROM
/* JOIN on matching teamID and non-matching player-id (so you don't get the same player twice) */
players p1 JOIN players p2 ON p1.teamID = p2.teamID and p1.id <> p2.id
);