I have two tables: cities and students. Each student has city_id which references a city in the citites table.
I have a join:
SELECT lastname, city, points_avg
FROM students INNER JOIN cities
ON students.city_id = cities.city_id where city LIKE 'Paris'
It works pretty well.
But then, I want to select the student with the highest score EXCLUSIVELY from Paris.
I tried this:
select lastname, points_avg from (
SELECT lastname, city, points_avg
FROM students INNER JOIN cities
ON students.city_id = cities.city_id where city LIKE 'Paris'
) AS join_name where points_avg = (select MAX(points_avg) from join_name);
But it says that current database doesnt have the table join_name
I wrote this:
select lastname, points_avg from (
SELECT lastname, city, points_avg
FROM students INNER JOIN cities
ON students.city_id = cities.city_id where city LIKE 'Paris'
) AS join_name where points_avg = (select MAX(points_avg) from (
SELECT lastname, city, points_avg
FROM students INNER JOIN cities
ON students.city_id = cities.city_id where city LIKE 'Paris'
) AS join_name_new);
And unexpectedly, it works, but it`s so stupid ahah
Could you advise me, how to get the same result without repeating join twice?
You can do this using a CTE, but not a subquery:
with join_name as (
select s.lastname, c.city, s.points_avg
from students s join
cities c
on s.city_id = c.city_id
where c.city like 'Paris'
)
select jn.lastname, jn.points_avg
from join_name jn
where jn.points_avg = (select max(jn2.points_avg) from join_name jn2);
If you are learning SQL, that is just the difference between a CTE and a subquery.
Also note the use of table aliases and qualified column references. They make it clearer what the query is really doing.
Also, I retained the structure of your query, but you rank() would be a more typical way to implement this logic.
#Cognosce, if you are learning SQL then #Gordon Linoff is correct!
However, a much simpler way to write same query without subquery and CTE would be:
select s.lastname, c.city, s.points_avg
from students s join
cities c
on s.city_id = c.city_id
where c.city like 'Paris'
Order by s.points_avg desc
Limit 1
Related
I have two tables: cities and states. States has columns for state codes and full name. Cities contains columns for population, state code, and the city name. My goal is to create a table of the city in each state with the highest population.
This is my solution which seems to work in a test, but I've been told that using max() is non-deterministic and I should use a window function instead.
SELECT
s.name,
c.name,
max(c.population)
FROM cities AS c
LEFT JOIN states AS s
ON c.state_code = s.code
GROUP BY s.name
ORDER BY s.name;
What is wrong with using max here, when would it give incorrect results?
In most databases your query would not even run, because you are selecting the non-aggregated column c.name without also using it in the GROUP BY clause.
For MySql, the code would run if ONLY_FULL_GROUP_BY mode is disabled, but still it would return wrong results because the query would pick a random city name out of all the cities of each state.
See the demo.
For SQLite, your query is correct!
SQLite's feature of bare columns, makes sure that the city name you get in the results is the one that has the max population.
This is non-standard, but it is documented.
The only problem here is that if there are 2 or more cities with the same max population you will get only one of them in the results.
See the demo.
You can find the city in each state with the max population and use it in a sub-query and join it with the tables.
Query
select s.name as state, c.name as city, c.population
from states s
join cities c
on c.state_code = s.code
join (
select state_code, max(population) as max_pop
from cities
group by state_code
) as p
on p.state_code = c.state_code
and p.max_pop = c.population;
create table states(code varchar(50),name varchar(50));
create table cities(code varchar(50),name varchar(50),population int, state_code varchar(50));
insert into states values('s01','state1');
insert into cities values('c01','city1',100,'s01');
insert into cities values('c02','city2',10,'s01');
Query:
with cte as
(
SELECT
s.name state_name,
c.name city_name,
c.population,
row_number()over(partition by s.name order by c.population desc)rn
FROM cities AS c
LEFT JOIN states AS s
ON c.state_code = s.code
)
select state_name, city_name, population from cte where rn=1
Output:
state_name
city_name
population
state1
city1
100
db<>fiddle here
I have a problem with a query.
I have to find for all the continent: the name of the continent, number of cities and number of countries. This is what I did
SELECT co.continent, COUNT(*)
FROM Country co
JOIN City c ON c.countrycode = co.code
GROUP BY co.continent
UNION
SELECT COUNT(*)
FROM Country co2
WHERE co.continent = co2.continent ( <---- ??? )
GROUP BY co2.continent
But I don't know if is it legal the part "WHERE co.continent = co2.continent" because the second query isn't a subquery of the first, is it? Is there another way to do this query?
UNION is not required, a single query with GROUP BY and COUNT aggregate will get the desired result, there could be multiple cities in the same country, a country could appear multiple times, use COUNT(DISTINCT...) to remove duplicates.
SELECT co.continent, COUNT(*) cities, COUNT(DISTINCT co.code) countries
FROM Country co
JOIN City c ON c.countrycode = co.code
GROUP BY co.continent
co.continent = co2.continent in the original union query is invalid. Queries in union are independent from each other.
I'm still learning the MySQl.
This is the relational DBMS :
CUSTOMER (CustID, CustName, AnnualRevenue)
TRUCK (TruckNumber, DriverName)
CITY (CityName, Population)
SHIPMENT (ShipmentNumber, CustID, Weight, Year, TruckNumber, CityName)
Now, I have to formulate for these two queries:
Total weight of shipments per year for each city.
Drivers who drove shipments to London but not Paris.
These are the queries i have came up with:
1.
select sum(s.weight), s.year , c.city
from shipment s, city c
INNER JOIN CITY
on s.CityName = c.CityName
You are mixing and old way to JOIN table (which you should avoid because the joining columns are not explicitly stated and it is confusing for others):
FROM shipment s, city c
You should group columns in the select that are not aggregated (year, city). Also it is better to use an alias for the aggregated column (AS total_weight)
select sum(s.weight) AS total_weight, s.year , c.city
from shipment s
INNER JOIN CITY as c
on s.CityName = c.CityName
GROUP BY s.year, c.city
Try to solve the second query and come back if you have a problem.
I have used UNION for 2 separate Mysql query, The query is as below.
select country,u_id from
(SELECT regionShortName as country , GROUP_CONCAT( urls_regions.u_id ) AS
u_id
FROM urls_regions
where LENGTH(regionShortName )<=2
GROUP by regionShortName)
UNION
(SELECT countries.name as country,
GROUP_CONCAT( urls_regions.u_id ) AS u_id
FROM `countries` countries
inner join regions on countries.regions_id = regions.id
INNER JOIN urls_regions ON regions.region = urls_regions.regionShortName
GROUP by countries.name, country)
The above query has given me the results as below,
I am not able to achieve as below,
Now I would like know how to group by the "country" by concat(merge or join) "u_id"
Is my query implemented is right approach or is there any other approach to achieve this.
Using below query I am able to get what I am expecting,
select tb.country, GROUP_CONCAT(tb.u_id, ',') from
((SELECT regionShortName as country , GROUP_CONCAT( urls_regions.u_id ) AS
u_id
FROM urls_regions
where LENGTH(regionShortName )<=2
GROUP by regionShortName)
union
(SELECT countries.name as country,
GROUP_CONCAT(urls_regions.u_id ) AS u_id
FROM `countries` countries
inner join regions on countries.regions_id = regions.id
INNER JOIN urls_regions ON regions.region = urls_regions.regionShortName
GROUP by countries.name, country)) tb group by tb.country
I have done 2 things
In my first line of query I have added GROUP_CONCAT(tb.u_id, ',')
In the last line added group by tb.country
I'm trying to clean a db with duplicate records. I need to move the reference to a single record and delete the other one.
I have two tables: Promoters and Venues, each has a reference to a table called cities. The problem is that there are cities with the same name and different ids, that have a relation with venues and promoters.
With this query I can group all promoters and venues with a single city record:
SELECT c.id as id, c.name as name, GROUP_CONCAT( DISTINCT p.id ) as promoters_ids, GROUP_CONCAT( DISTINCT v.id ) as venues_ids
FROM cities as c
LEFT JOIN promoters as p ON p.city_id = c.id
LEFT JOIN venues as v ON v.city_id = c.id
WHERE c.name IN ( SELECT name from cities group by name having count(cities.name) > 1 )
GROUP BY c.name
Now I want to run an UPDATE query on promoters, setting the city_id equals to the result of the query above.
Something like this:
UPDATE promoters AS pr SET pr.city_id = (
SELECT ID
FROM (
SELECT c.id as id, c.name as name, GROUP_CONCAT( DISTINCT p.id ) as promoters_ids
FROM cities as c
LEFT JOIN promoters as p ON p.city_id = c.id
WHERE c.name IN ( SELECT name from cities group by name having count(cities.name) > 1 ) AND pr.id IN promoters_ids
GROUP BY c.name
) AS T1
)
How can I do this?
Thanks
If I understand correctly, you want to remove duplicate cities (in the end), so you need to update promoters that are linked to any of the cities you want to remove in that process.
I think it makes sense to use the lowest ID of any of the cities with the same name (could be the highest just as well, but I want to specify it at least, and don't leave it up to me.
So in order get the right ID for a promoter, I need to: Select the lowest ID of all cities that have the same name as the city already linked to a promoter.
Fortunately, that demand fits snuggly into a query:
UPDATE promoters AS pr
SET pr.city_id = (
SELECT
-- Select the lowest ID ..
Min(c.id)
FROM
-- .. of all cities ..
Cities c
-- .. that have the same name ..
INNER JOIN Cities pc on pc.Name = c.Name
WHERE
.. as the city already linked to the promoter being updated
pc.id = pr.city_id
GROUP BY
c.name)
The trick is to join Cities on itself by name, so you can easily get all cities with the same name. I think you tried the same with the IN clause, but that's a little more complex than it needs to be.
I don't think you need group_concat at all, besides checking if the inned query returns the correct cities indeed, although it doesn't make sense, since you're already grouping on the name. When written like this, you can tell that there should be no way that this can go wrong:
SELECT
-- Select the lowest ID ..
MIN(c.id) AS id,
GROUP_CONCAT(c.name) AS names --< already grouped by this, so why...
FROM
-- .. of all cities ..
Cities c
-- .. that have the same name.
INNER JOIN Cities pc on pc.Name = c.Name
GROUP BY
c.name
I hope I understood the question correctly.