SQL For each statement - mysql

I am working on some SQL homework and could someone explain to me how to get this question done.
Display the average raw scores of team ‘Dolphins (1 point)
Here is an image of the data structure.
I need to make a query that returns the average raw score of 4 players.
However, when I try executing the code below it just returns one average.
/* Question 2 */
SELECT AVG(RawScore)
FROM Bowler_Scores
WHERE BowlerID IN
(
SELECT BowlerID
FROM Bowlers
WHERE TeamID =
(
SELECT TeamID
FROM Teams
WHERE TeamName = "Dolphins"));
In bowler scores each bowler id can have multiple scores.
For instance it may have the records - (43,101) (50,301) and (43,106).
I don't know how to write and sql statement that will get the average raw score for each player on that team out of all of there individual raw scores in the bowler scores table.

If you need the average individual scores for each member of the Dolphins Team you can use this:
Select Teams.TeamName, Bowlers.BowlerID, avg(Rawscore)
from Bowlers
inner join Teams
on Bowlers.TeamId = Teams.TeamID
inner join Bowler_Scores
on Bowlers.BowlerID = Bowler_Scores.BowlerID
where teams.teamname = 'Dolphins'
group by TeamName, BowlerID
If you just need one average score for the team then just remove the BowlerID from the SELECT and GROUP BY lines.

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

Use SELECT through three table

I tried to write a query, but unfortunately I didn't succeed.
I want to know how many packages delivered over a given period by a person.
So I want to know how many packages were delivered by John (user_id = 1) between 01-02-18 and 28-02-18. John drives another car (another plate_id) every day.
(orders_drivers.user_id, plates.plate_name, orders.delivery_date, orders.package_amount)
I have 3 table:
orders with plate_id delivery_date package_amount
plates with plate_id plate_name
orders_drivers with plate_id plate_date user_id
I tried some solutions but didn't get the expected result. Thanks!
Try using JOINS as shown below:
SELECT SUM(o.package_amount)
FROM orders o INNER JOIN orders_drivers od
ON o.plate_id=od.plate_id
WHERE od.user_id=<the_user_id>;
See MySQL Join Made Easy for insight.
You can also use a subquery:
SELECT SUM(o.package_amount)
FROM orders o
WHERE EXISTS (SELECT 1
FROM orders_drivers od
WHERE user_id=<user_id> AND o.plate_id=od.plate_id);
SELECT sum(orders.package_amount) AS amount
FROM orders
LEFT JOIN plates ON orders.plate_id = orders_drivers.plate_id
LEFT JOIN orders_driver ON orders.plate_id = orders_drivers.plate_id
WHERE orders.delivery_date > date1 AND orders.delivery_date < date2 AND orders_driver.user_id = userid
GROUP BY orders_drivers.user_id
But seriously, you need to ask questions that makes more sense.
sum is a function to add all values that has been grouped by GROUP BY.
LEFT JOIN connects all tables by id = id. Any other join can do this in this case, as all ids are unique (at least I hope).
WHERE, where you give the dates and user.
And GROUP BY userid, so if there are more records of the same id, they are returned as one (and summed by their pack amount.)
With the AS, your result is returned under the name 'amount',
If you want the total of packageamount by user in a period, you can use this query:
UPDATE: add a where clause on user_id, to retrieve John related data
SELECT od.user_id
, p.plate_name
, SUM(o.package_amount) AS TotalPackageAmount
FROM orders_drivers od
JOIN plates p
ON o.plate_id = od.plate_id
JOIN orders o
ON o.plate_id = od.plate_id
WHERE o.delivery_date BETWEEN convert(datetime,01/02/2018,103) AND convert(datetime,28/02/2018,103)
AND od.user_id = 1
GROUP BY od.user_id
, p.plate_name
It groups rows on user_id and plate_name, filter a period of delivery_date(s) and then calculate the sum of packageamount for the group

How to join on a row with max value

I have three tables: households, voters, door_knocks
Each household can have several voters associated with it. Each household can also have several door knocks associated with it.
I'm trying to pull together all the voters in a household and the date of the last door_knock from the door_knocks table and I'm having trouble figuring out the proper query syntax. Here is my latest attempt:
SELECT households.hh_id, voters.id
FROM households
INNER JOIN voters ON households.hh_id = voters.hh_id
INNER JOIN ( SELECT MAX(dk.date), dk.hh_id FROM door_knocks dk GROUP BY dk.date) dks
ON dks.hh_id = households.hh_id
WHERE households.street = ?
The above query pulls up one result for each door knock, however. I just want the the date from the last door knock.
So, what it sounds like you're hoping for conceptually is a table that lists the last date of a knock for each houshold.
You'd like to join against that table and combine it with the voters and the households.
what your query does is give you a table of all the dates (group by dk.date) and for each date list all the households.
If you group by hh_id instead, then you will get the max date for each given household.
SELECT households.hh_id, voters.id, dks.max_date
FROM households
INNER JOIN voters ON households.hh_id = voters.hh_id
INNER JOIN ( SELECT MAX(dk.date) as max_date, dk.hh_id FROM door_knocks dk GROUP BY dk.hh_id dks
ON dks.hh_id = households.hh_id
WHERE households.street = ?

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

SQL query - Get rows only if value falls within range of "last n records" (record-specific)

I have two tables, a Countries table and a Weather table. I would like to retrieve all of the names of countries where it has not rained within the last 15 days.
The weather table has a column called "DayNum", which goes from 1 -> infinity and increases by 1 on each day, it is unique. This table also has a column called "Rain" which is just a bit boolean value of 0 or 1.
Also, not all Countries were added on the same day, so the max DayNum will be different for each country.
Examples of tables below (data is snipped for readability):
Countries:
ID Name
1 USA
2 Cananda
3 Brazil
Weather
ID Country_id DayNum Rain
1 1 1 0
2 1 2 0
3 1 3 1
Here is my current attempt at a query (been working on this for days):
SELECT countries.name, weather.daynum
FROM countries INNER JOIN weather ON countries.id = weather.country_id
GROUP BY countries.name
HAVING weather.daynum > (MAX(weather.day_num) - 15) AND SUM(weather.rain) = 0;
I think this should work, but I'm having serious performance issues. The actual query I need to write deals with different data (same exact concept) and millions of rows. This query seems to get slower at an exponential rate.
Can anyone offer any advice?
Another idea I had was to somehow limit the JOIN to only grab the top 15 records (whilst ORDERing BY weather.day_num), but I Haven't found a way to do this within a JOIN (if it's even possible).
You're not interested in the amount of rain, just whether it exists, so...
select * from countries
left join
(
select weather.country_id
from weather
inner join
(select country_id, MAX(daynum) as maxdaynum from weather group by country_id) maxday
on weather.country_id = maxday.country_id
and weather.daynum>maxday.maxdaynum-3
where rain=1
) rainy
on countries.id = rainy.country_id
where country_id is null
I presume you've already indexed your tables appropriately
You didn't include any information about the indices on your tables, but I'm betting the performance issues you are experiencing are related to the group by on the countries name field. It would certainly explain your performance issues if that column isn't indexed.
Having said that, this is a situation that probably calls for a subquery rather than an inner join. I would be tempted to write the query this way:
SELECT countries.id, countries.name
FROM countries
INNER JOIN
(
SELECT country_id
FROM weather
GROUP BY country_id
HAVING weather.daynum > (MAX(weather.day_num) - 15) AND SUM(weather.rain) = 0
) AS weather
ON weather.country_id = countries.id;
I have two tables, a Countries table and a Weather table. I would like to retrieve all of the names of countries where it has not rained within the last 15 days.
Here you go:
SELECT * FROM Country
WHERE
NOT EXISTS (
SELECT * FROM Weather
WHERE
Rain = 1
AND DayNum >= 2
AND Country_id = Country.ID
);
In plan English: for each country, check if there are any rainy days newer than the given day number. If there are, eliminate the country from the result.
Replace 2 with the day number 15 days ago. Index on {Country_id, DayNum, Rain} for decent performance. Unfortunately, MySQL is unlikely to execute this query optimally, but there are only so many countries so nested loops shouldn't be too bad since DBMS should be able to execute the inner query as a single index seek.
Alternatively, consider rewriting it as JOIN, for example:
SELECT Country.*
FROM Country LEFT JOIN Weather
ON Country_id = Country.ID
AND Rain = 1
AND DayNum >= 2
GROUP BY Country.ID, Country.Name
HAVING MAX(Rain) IS NULL OR MAX(Rain) = 0;
A working SQL Fiddle example is here.
Perhaps you can use a simple variable to store the min daynum required ? I am not a mySQL developer, but something like that will do the trick I think :
SELECT #minDaynum := (MAX(daynum)-15) FROM weather;
SELECT DISTINCT countries.name
FROM weather
INNER JOIN countries ON weather.country_id = countries.id
WHERE
weather.daynum >= #minDaynum AND
weather.rain = 1;
EDIT >> If just one variable doesn't work for your case, maybe try using a temporary table to speed things up (not sure if performances of temporary tables in mysql are really good though...) :
CREATE TEMPORARY TABLE min_daynums (country_id int, country_name, min_daynum int);
INSERT INTO min_daynum
SELECT countries.id, countries.name, MAX(weather.daynum)-15
FROM weather
INNER JOIN countries ON countries.id = weather.country_id
GROUP BY countries.id, countries.name
SELECT min_daynums.country_name
FROM min_daynums
WHERE
EXISTS(
SELECT 1
FROM weather
WHERE
weather.country_id = min_daynums.country_id
and weather.daynum >= min_daynums.min_daynum
and weather.rain = 1
)
Here I just store the min daynum for each country in a temp table. Hope it helps...