Group By SQL Statement - Get countries with more than 5 cities - sql

I am trying to pull the countries that have more than 5 cities.
Tables:
City city_id, city, country_id, last_update
Country country_id, country, last_update
I think I am very close to getting this figured out, but I'm not quite there. Any pointers?
SELECT DISTINCT country
FROM country C, city O
WHERE O.country_id = C.country_id AND O.country_id
IN (SELECT country_id FROM city group by country_id having count(country_id) > 5);

select country
from country inner join city on city.country_id = country.country_id
group by country
having count(distinct city) > 5

Use the below query..
SELECT country
FROM country C
JOIN city O
ON O.country_id = C.country_id
GROUP BY country
HAVING count(distinct O.city)>5

you can try like this
select countryid, count(distinct city_id) from country c join city ct c.country_id=ct.country_id
group by countryid
having count(distinct city_id) > 5

SELECT DISTINCT
country
FROM country C
WHERE EXISTS (SELECT COUNT(DISTINCT city_id)
FROM city CITY
WHERE CITY.country_id = C.country_id
GROUP BY CITY.country_id
HAVING COUNT(DISTINCT city_id) > 5);

The below query works for your requirement.
SELECT C.country
FROM country C
INNER JOIN
city O
ON O.country_id = C.country_id
GROUP BY C.country
HAVING count(distinct O.city)>5;

Related

Why Oracle OFFSET and LIMIT is not working when using DISTINCT

I need to get clarified the below situation.
I have a city, country table and I need to validate this will some other tables and get the city country results ordered by the country. Here's my query for that
SELECT distinct
c.code as CITY ,
c.country as COUNTRY from location_info li
inner join someTable s on li.loc_id = s.some_id
inner join city c on s.city = c.code
ORDER BY c.country
And this provides the results as
Now when I use OFFSET and LIMIT values in the below query
SELECT distinct
c.code as CITY,
c.country as COUNTRY from location_info li
inner join someTable s on li.loc_id = s.some_id
inner join city c on s.city = c.code
ORDER BY c.country OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY.
I expect to get ADOR, ADPC, ADSJ, ADVD, ALV as the output.
Why is that and what I am missing here in this query.
When I do the following I get the expected outcome
SELECT CITY, COUNTRY FROM ( SELECT distinct
c.code as CITY,
c.country as COUNTRY from location_info li
inner join someTable s on li.loc_id = s.some_id
inner join city c on s.city = c.code
ORDER BY c.country ) OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY.
The issue is that the ORDER BY is not stable. What that means is that you have ties in the key values. So, running the query two times might result in different orderings.
This is easily fixed by including more keys in the order by so the order by keys uniquely identify each row:
select distinct c.code as CITY, c.country as COUNTRY
from location_info li join
someTable s
on li.loc_id = s.some_id join
city c
on s.city = c.code
order by c.country, c.code;

Not unique table/alias?

I get this error: Not unique table/alias: 'postcode'
"SELECT sub.city AS city, sub.postalcode AS postalcode FROM
(SELECT postcode.city AS city, customers.postalcode AS postalcode, COUNT(customers.postalcode) AS postcode_numbers
FROM orders, postcode
INNER JOIN customers ON orders.userID = customers.ID
INNER JOIN postcode ON customers.postalcode = postcode.city
GROUP BY customers.postalcode) sub
WHERE sub.postcode_numbers > 3";
Remove postcode table in the from statement
SELECT sub.city AS city, sub.postalcode AS postalcode FROM
(SELECT postcode.city AS city, customers.postalcode AS postalcode, COUNT(customers.postalcode) AS postcode_numbers
FROM orders
INNER JOIN customers ON orders.userID = customers.ID
INNER JOIN postcode ON customers.postalcode = postcode.city
GROUP BY customers.postalcode) sub
WHERE sub.postcode_numbers > 3
You have postcode in both your from clause and your second join clause. Remove it from the from clause and you should be OK:
SELECT sub.city AS city, sub.postalcode AS postalcode FROM
(SELECT postcode.city AS city, customers.postalcode AS postalcode, COUNT(customers.postalcode) AS postcode_numbers
FROM orders -- Removed postcode from here
INNER JOIN customers ON orders.userID = customers.ID
INNER JOIN postcode ON customers.postalcode = postcode.city
GROUP BY customers.postalcode) sub
WHERE sub.postcode_numbers > 3
You can write this as:
SELECT p.city, c.postalcode , COUNT(*) AS num_orders
FROM orders o JOIN
customers c
ON o.userID = c.ID JOIN
postcode p
ON c.postalcode = p.city
GROUP BY p.city, c.postalcode
HAVING COUNT(*) > 3;
It is unclear whether you want one row per city or per city/postalcode. This assumes the latter. If you want one row per city, remove postalcode from both the SELECT and the GROUP BY.
Notes:
Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax. That is your specific problem.
Table aliases make the query easier to write and to read.
The unaggregated columns in the SELECT should match the GROUP BY keys.
A subquery is not necessary.
You are using postcode table in both FROM clause and and INNER JOIN clause, neither of which have an alias, when you do an INNER JOIN you don't need to have that table in the FROM clause, and other way around of course.
If you want to join the tables in FROM clause you would do something like this:
SELECT
sub.city AS city, sub.postalcode AS postalcode
FROM
(SELECT
postcode.city AS city, customers.postalcode AS postalcode,
COUNT(customers.postalcode) AS postcode_numbers
FROM
orders, postcode
INNER JOIN
customers ON orders.userID = customers.ID
--INNER JOIN postcode ON customers.postalcode = postcode.city
WHERE
postcode.city = customers.postalcode
GROUP BY
customers.postalcode) sub
WHERE
sub.postcode_numbers > 3;
So basically you would lose the INNER JOIN postcode... line
The other way to do this is to use only the INNER JOIN clause, which is my preferred way. To do this you only need to lose the postcode in FROM clause, like so:
SELECT
sub.city AS city, sub.postalcode AS postalcode
FROM
(SELECT
postcode.city AS city, customers.postalcode AS postalcode,
COUNT(customers.postalcode) AS postcode_numbers
FROM
orders
INNER JOIN
customers ON orders.userID = customers.ID
INNER JOIN
postcode ON customers.postalcode = postcode.city
GROUP BY
customers.postalcode) sub
WHERE
sub.postcode_numbers > 3;
Hope this helps!

Using Oracle in select query

Question: Given the CITY and COUNTRY tables, query the sum of the populations of all cities where the CONTINENT is 'Asia'.
City table contains fields: CountryCode, Population
Country table contains fields: Code, Continent
(CITY.CountryCode and COUNTRY.Code are matching key columns.)
I tried the following query: (I know this can be solved using Inner join)
Select sum(city.population) from city
where city.countrycode in (Select code from Country where continent = 'Asia')
Hacker rank gives following error:
ERROR at line 3:
ORA-00933: SQL command not properly ended
Do you need a semi colon?
Something along these lines......
Per city..........
SELECT City.Name, SUM(City.Population)
FROM City INNER JOIN Country ON Country.Code = City.CountryCode
WHERE
Country.Continent = 'ASIA'
GROUP BY
City.Name;
Per Country & City
SELECT Country.Name, City.Name, SUM(City.Population)
FROM City INNER JOIN Country ON Country.Code = City.CountryCode
WHERE
Country.Continent = 'ASIA'
GROUP BY
Country.Name, City.Name;
Just the Total for ASIA
SELECT SUM(City.Population)
FROM City INNER JOIN Country ON Country.Code = City.CountryCode
WHERE
Country.Continent = 'ASIA';
What we are doing here is querying the city table and finding all matches in the country table by using the identifying field in both those tables:
country.code in your country table
city.countrycode in your city table
Whenever country.code = city.countrycode and country.continent= 'Asia', you will be returned the sum of that population.
Select sum(city.population) from city inner join country on country.code = city.countrycode
where country.continent= 'Asia';
I also recommend you select the city the population count belongs to:
Select city.name, sum(city.population) from city inner join country on country.code = city.countrycode
where country.continent= 'Asia';
This is the right code
Select sum(city.population)
from city inner join country on country.code = city.countrycode
where country.continent= 'Asia';
Select SUM(City.POPULATION) from City join Country ON city.countrycode=country.code
where country.continent='Asia'
Use ; at the end of the code.
Select sum(city.population) from city
where city.countrycode in (Select code from Country where continent = 'Asia');

For each country, find city that has the highest population and the city's population

I am using the Mondial database schema and am trying to find: For each country, find city that has the highest population and the city's population.
Right now I have:
SELECT Country.Name, city.name, MAX(city.population) Population
FROM city
Join Country
On Country.Code=City.Country
WHERE city.population IS NOT NULL
GROUP BY Country.Name, city.name
ORDER BY Country.Name;
This gives me ALL of the cities in each country and their populations and not just the largest city.
Use analytical functions. Something like this should work (untested):
select
country.name,
city.name,
city.population
from
country
join
(
select
country,
name,
population,
row_number() over ( partition by population desc) as rn
from
city
) city on
city.country = country.code
and city.rn = 1
order by
country.name
Don't know in oracle but if done in SQL Server it can be done like this:
Select * from
(select
Country.Name,
city.name,
city.population,
ROW_NUMBER() over(partition by Country.Name order by Country.Name,city.population desc) RowNum
from Country inner join city city on Country.Code=City.Country) tbl
where RowNum = 1
function similar to row_number in oracle will help.
Hope This help.
This seems to work.
It's also useful for filtering query results according to column containing an aggregate function.
SELECT ct.name AS "Country", c1.name AS "City", c1.population AS "Population"
FROM city c1
JOIN country ct
ON c1.country = ct.code
WHERE c1.population = (SELECT max(population)
FROM city c2
WHERE c1.country = c2.country)
ORDER BY country
Here you have already done Group by Country.name so you can just have single country detail, so instead of going for the MAX(population) you can just do order by city.population also remove the group by for city.name
E.g.
SELECT Country.Name, city.name, city.population Population
FROM city
Join Country
On Country.Code=City.countrycode
WHERE city.population IS NOT NULL
GROUP BY Country.Name
ORDER BY city.population desc;
This will not give you the countries in sorted order but that can also be done after adding another order by on top of it if you really want country name also sorted.
SELECT Country.Name, city.name, city.population Population
FROM city
Join Country
On Country.Code=City.countrycode
WHERE city.population IS NOT NULL
GROUP BY Country.Name
ORDER BY country.name, city.population desc;
Hope that helps to simplify the SQL query. This I have tested in MySQL.
You cannot use MAX in multiple select
try this:
SELECT Country.Name, city.name, city.population
FROM city
Join Country
On Country.Code=City.Country
WHERE city.population IS NOT NULL and city.population in (SELECT MAX(population) FROM city limit 1)
GROUP BY Country.Name, city.name
ORDER BY Country.Name;

SQL query - IS NULL

Tables:
Country
-------
PK CountryID
Name
City
-------
PK CityID
FK CountryID
Name
Airport
--------
PK AirportID
FK CityID
Name
My task is to select names of countries that have no airport.
I can imagine only one solution with EXCEPT (or MINUS)
SELECT Country.Name
FROM Country EXCEPT (SELECT DISTINCT Country.Name FROM Country, City, Airport
WHERE City.CountryID = Country.CountryID AND Airport.CityID = City.CityID);
But is it possible not to use EXCEPT but something like IS NULL?
SELECT cn.CountryID
FROM Country cn
LEFT JOIN City ct ON cn.CountryID = ct.CountryID
LEFT JOIN Airport ar on ar.CityID=ct.CityID
WHERE ar.AirportID is null
If you need to make this query with IS NULL try following query:
SQLFiddle demo
select ct.CountryId,max(ct.Name) from Country ct
left join City c on ct.CountryId=c.CountryId
left join Airport a on a.CityId=c.CityID
group by ct.CountryId
HAVING max(a.AirportID) IS NULL
You can use this, but it's basically the same thing you have with different syntax.
SELECT Country.Name
FROM Country
Where Country.Name Not IN
(
SELECT DISTINCT Country.Name FROM Country, City, Airport
WHERE City.CountryID = Country.CountryID AND Airport.CityID = City.CityID
);