How do I join two grouped up SELECT Querys in SQL? - mysql

There are just two relations important for this:
geoLake with Name and Country
geoMountain with Name and Country
Both relations having couple hundreds of Entries.
The Task is to just display the names of the countrys which have more lakes than mountains.
SELECT m.Country, Count(m.Country)
FROM geoMountain m
GROUP BY m.Country
Shows a list with all Countrynames and the Number of how many Mountains are related to each country.
SELECT l.Country, Count(l.Country)
FROM geoLake l
GROUP BY l.Country
Having the same Output for how many Lakes are in every Country.
I tried like everthing to bring this two grouped relations together, but not having any success and kinda stucked after like 2 hours, because I am running out of ideas.
How do I bring this together?
My specific Questions:
Is it possible to get a Relation like:
+--------+-------------------+----------------+
|Country |COUNT(m.Country) |COUNT(l.Country)|
+--------+-------------------+----------------+
|Country1|How many Mountains |How many Lakes |
|Country2|How many Mountains |How many Lakes |
[...]
And how do I add a SELECT query on top of this with this
SELECT Country FROM What is build up there WHERE COUNT(m.Country) > COUNT(l.Country)
mechanic.
PS. Hope my question is understandable, english isn't my native language.

WITH
-- count the amount of mountains
cte1 AS (
SELECT m.Country, Count(m.Country) cnt
FROM geoMountain m
GROUP BY m.Country
),
-- count the amount of lakes
cte2 AS (
SELECT l.Country, Count(l.Country) cnt
FROM geoLake l
GROUP BY l.Country
),
-- gather countries which are mentioned in at least one table
cte3 AS (
SELECT Country FROM cte1
UNION
SELECT Country FROM cte2
)
-- get needed data
SELECT Country,
COALESCE(cte1.cnt, 0) AS MountainsAmount,
COALESCE(cte2.cnt, 0) AS LakesAmount
-- join data to complete countries list
FROM cte3
LEFT JOIN cte2 USING (Country)
LEFT JOIN cte1 USING (Country)
-- HAVING MountainsAmount > LakesAmount
;

Related

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

SQL: What country has the most cities?

Hello I started using MySQL and I seem to be having trouble trying to nest formulas. I'm working on a problem and the question is, What country has the most cities?
I have two tables:
CITY:
city
city_id
country_id
COUNTRY:
country
country_id
I am able to join the two tables together to get the cities to match with the countries but after that I don't know how to count to the country that has the most cities.
My current code is:
SELECT city.city, country.country
FROM city, country
WHERE city.country_id = country.country_id
From there I don't know how to add a count function without it coming back as as error. I dont fully understand the basics of nesting.
Thank you, any help is appreciated.
You do not need to do nesting necessarily. To simply know, which country has most number of cities, just use group by:
select country_id, count(1)
from city
group by country_id
This will give you the number of cities in each country. Then you could use a CTE to get the country with the largest number of cities.
You need to GROUP BY if you want to use aggregate functions.
Given the fact that you're very new to this I think you'll get a lot more out of this if you spend a few minutes reading up on some documentation. Don't worry, this is easy stuff so you'll understand this in no time. Please have a look at the following basic info (MySQL GROUP BY basic info) regarding the use of GROUP BY in MySQL. Your questions are answered in the topic regarding 'MySQL GROUP BY with aggregate functions'.
Basic group by:
SELECT
status, COUNT(*)
FROM
orders
GROUP BY status;
Group by using a join:
SELECT
status, SUM(quantityOrdered * priceEach) AS amount
FROM
orders
INNER JOIN
orderdetails USING (orderNumber)
GROUP BY status;
SELECT x.country
FROM country x
JOIN city y
ON y.country_id = x.country_id
GROUP
BY x.country
ORDER
BY COUNT(*) DESC LIMIT 1;
On the (fantastically unlikely) chance that the most civilised countries have equal numbers of cities, you would have to amend this a little.
What makes this difficult is possible ties, i.e. two or more countries sharing the maximum number of cities. As of MySQL 8 you can use window functions to help you with this. Here I compare the country counts and their maximum and then pick the rows were the two match.
select *
from country
where (country_id, true) in -- true means it is a maximum city country
(
select country_id, count(*) = max(count(*)) over()
from city
group by country_id
);
Sorry it was my first post, my code looks terrible.
following my code again.
select
country.country_id,
count(city.city_id)
from
country
inner join
city
on
city.country_id=country.country_id
group by
city.country_id
having
count(city.city_id) =
(SELECT
max(count(city.city_id))
FROM
city
GROUP BY
city.city_id);
Best regards,
Jens
Try the following code:
**select
country.country_id,
count(city.city_id)
from
country
inner join
city
on
city.country_id=country.country_id
group by
city.country_id
having
count(city.city_id) =
(SELECT
max(count(city.city_id))
FROM
city
GROUP BY
city.city_id);**
You need to group by country_id to make sure all cities that are connected to one country_id can be counted.
Where is a good approach, however it does not work together with "group by" as it will be accounted before the "group by" command.
The way you joined your data from different tables is not a very proper yet working way. I suggest to use the inner join in this case to make the command more obvious/better readable.
Count() is used to count the number of cities that accumulate on one country
max() is used to get the country with the most cities (highest count()).
SELECT country_id, count(1)
FROM city
GROUP BY country_id
ORDER BY count(1) desc;
Try following Code -
SELECT *,
(SELECT COUNT(*) FROM cities WHERE cities.country_id=C.country_id) as cities_count
FROM country C
ORDER BY cities_count DESC
LIMIT 0,1
Also is joining the two tables necessary? In your query, you said you need to find What country has the most cities?
Above query will only return one country with max cities.
You can find the country like this:
SELECT MAX(c.id) FROM (SELECT COUNT(id) AS id
FROM city group by country_id) c

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...

MySQL Join Query

I need to query the database by joining two tables. Here is what I have:
Table Town:
id
name
region
Table Supplier:
id
name
town_id
I currently have the following query which outputs all the Towns that belong to a given region:
SELECT id, name FROM Town WHERE region = 'North West';
Now I need to extend this query and create two further queries as follows:
Output the number of Suppliers for each Town
Output only the Towns that have 1 or more Supplier
I am using PHP for my scripts if that helps. I know I may be able to to get this data using PHP but in terms of performance it will probably be better if it is done in MySQL.
EDIT (27/07/10):
I now needs to extend this one last time - there is another table called Supplier_vehicles:
id
supplier_id
vehicle_id
A Supplier can have many Supplier_vehicles. The count (NumSupplier in this case) needs to now contain the total number of suppliers in a given town that have any of the given vehicle_id (IN condition):
SELECT * FROM Supplier s, Supplier_vehicles v WHERE s.id = v.supplier_id AND v.vehicle_id IN (1, 4, 6)
Need to integrate the above query into the existing JOIN query.
Count the number of suppliers.
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
LEFT OUTER JOIN Suppliers s ON s.town_id = t.id
GROUP BY t.id, t.name
Only towns that have at least one supplier
SELECT DISTINCT t.id, t.name
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
And you are 100% correct, the best place for this is an SQL query.
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
LEFT JOIN Suppliers s
[WHERE NumSupplier > 1]
GROUP BY t.id