MySQL world database Trying to avoid subquery - sql

I'm using the MySQL WORLD database.
For each Continent, I want to return the Name of the country with the largest population.
I was able to come up with a query that works. Trying to find another query that uses join only and avoid the subquery.
Is there a way to write this query using JOIN?
SELECT Continent, Name
FROM Country c1
WHERE Population >= ALL (SELECT Population FROM Country c2 WHERE c1.continent = c2.continent);
+---------------+----------------------------------------------+
| Continent | Nanme |
+---------------+----------------------------------------------+
| Oceania | Australia |
| South America | Brazil |
| Asia | China |
| Africa | Nigeria |
| Europe | Russian Federation |
| North America | United States |
| Antarctica | Antarctica |
| Antarctica | Bouvet Island |
| Antarctica | South Georgia and the South Sandwich Islands |
| Antarctica | Heard Island and McDonald Islands |
| Antarctica | French Southern territories |
+---------------+----------------------------------------------+
11 rows in set (0.14 sec)

This is the "greatest-n-per-group" problem that comes up frequently on StackOverflow.
SELECT c1.Continent, c1.Name
FROM Country c1
LEFT OUTER JOIN Country c2
ON (c1.continent = c2.continent AND c1.Population < c2.Population)
WHERE c2.continent IS NULL;
Explanation: do a join looking for a country c2 that has the same continent and a greater population. If you can't find one (which is indicated by the outer join returning NULL for all columns of c2) then c1 must be the country with the highest population on that continent.
Note that this can find more than one country per continent, if there's a tie for the #1 position. In other words, there could be two countries for which no third country exists with a greater population.

Related

Nested select with clause with AS operator

I'm learning nested select and I've encountered a problem with AS operator within the second (i.e. nested select).
Please have a look at the following table (truncated):
+-------------+-----------+---------+------------+--------------+
| name | continent | area | population | gdp |
+-------------+-----------+---------+------------+--------------+
| Afghanistan | Asia | 652230 | 25500100 | 20343000000 |
| Albania | Europe | 28748 | 2831741 | 12960000000 |
| Algeria | Africa | 2381741 | 37100000 | 188681000000 |
| Andorra | Europe | 468 | 78115 | 3712000000 |
| Angola | Africa | 1246700 | 20609294 | 100990000000 |
+-------------+-----------+---------+------------+--------------+
The aim is to show the countries in Europe with a per capita GDP greater than that of United Kingdom's. (Per capita GDP is the gdp/population).
The following query is correct in terms of syntax but it will not give the correct result as it selects gdp instead of gdp/population:
SELECT name
FROM world
WHERE gdp/population >
(SELECT gdp
FROM world
WHERE name = 'United Kingdom')
AND continent = 'Europe';
One solution to correct this would be using gdp/population instead of gdp in nested select but the resulting query would be incorrect in terms of syntax. Why? I use MariaDB but I'd like the query to be not dependent on DBMS provider.
SELECT name
FROM world
WHERE gdp/population >
(SELECT gdp AS gdp/population
FROM world
WHERE name = 'United Kingdom')
AND continent = 'Europe';
AS syntax is
SELECT expression AS ALIAS
So you got it the wrong way round, and the alias you are defining contains an illegal character (/). An alias is not required in this case, so you can simply do:
SELECT name
FROM world
WHERE gdp/population >
(SELECT gdp/population
FROM world
WHERE name = 'United Kingdom')
AND continent = 'Europe';

How does the ALL selector work in SQL?

I'm doing SQL Zoo and go stuck with this question:
List each continent and the name of the country that comes first alphabetically.
Here's a sample of the table:
+-------------+-----------+---------+
| name | continent | area |
+-------------+-----------+---------+
| Afghanistan | Asia | 652230 |
| Albania | Europe | 2831741 |
| Algeria | Africa | 28748 |
| ... | ... | ... |
+-------------+-----------+---------+
the use of ALL in this case. Here's the solution:
SELECT continent, name
FROM world x
WHERE name <= ALL (
SELECT name FROM world y
WHERE x.continent= y.continent)
Is this saying to find the name where name is "smaller" then all of the values found in matching continents?
Does this not answer the question?
select continent
, min(name) "First Alphabetical"
from world
group by continent

Access SQL - Return unique combinations in field

I have a table with data stored vertically, I have shown a simplified example below which has a record for each city a customer has lived in:
| CUSTOMER | CITY |
------------------------------
| John | London |
| John | Manchester |
| Sarah | Cardiff |
| Sarah | Edinburgh |
| Sarah | Liverpool |
| Craig | Manchester |
| Craig | London |
I am trying to come up with an SQL query that will return all unique combinations of cities so in the example above, John and Craig have both lived in London and Manchester but Sarah has lived in different cities (Cardiff, Edinburgh and Liverpool) so I would like an output as below (which can handle any amount of cities)
| CITY1 | CITY2 | CITY3 |
--------------------------------------------
| London | Manchester | |
| Cardiff | Edinburgh | Liverpool |
I have tried using a crosstab query to view the data horizontally like this:
TRANSFORM Max(City)
SELECT Customer
FROM tblCities
GROUP BY Customer
PIVOT City
but it is just returning a field for all cities for every customer. Does anyone know if this is possible using SQL?
p.s Ideally it will ignore the order of cities
This was a nice challenge! The query below gets the groupings per customer. It doesn't discard the duplicates where multiple customers have lived in the same combination of cities ... I'll let you or others find a way to handle that.
TRANSFORM Min(OrderedList.City) AS MinOfCity
SELECT OrderedList.Customer
FROM (SELECT CustomerCities.Customer, CustomerCities.City, Count(1) AS CityNo
FROM CustomerCities INNER JOIN CustomerCities AS CustomerCities_1 ON CustomerCities.Customer = CustomerCities_1.Customer
WHERE (((CustomerCities.City)>=[CustomerCities_1].[City]))
GROUP BY CustomerCities.Customer, CustomerCities.City) OrderedList
GROUP BY OrderedList.Customer
PIVOT "CITY" & [CityNo];
Is this what you want?
select distinct c1.city, c2.city
from tblCities as c1 inner join
tblCities as c2
on c1.customer = c2.customer and c1.city < c2.city;
This returns all pairs of cities that appear for any single customer.
Here is a query which might work assuming each customer is only associated with two cities:
SELECT DISTINCT t.city_1, t.city_2
FROM
(
SELECT MIN(CITY) AS city_1, MAX(CITY) AS city_2
FROM tblCities
GROUP BY CUSTOMER
) t

How to retrieve desired records?

Input Table : Regions
+---------------+---------------+---------- +-----------+
| Child | Parent | Level | levelname|
+---------------+---------------+---------- +-----------+
| All Region | All Region | 1 | national |
| Africa Region | All Region | 2 | region |
| America | All Region | 2 | region |
| Asia | All Region | 2 | region |
| Europe Region | All Region | 2 | region |
| Africa | Africa Region | 3 | Subregion |
| Asia Pacific | Asia | 3 | Subregion |
| Europe | Europe Region | 3 | Subregion |
| North America | America | 3 | Subregion |
| South America | America | 3 | Subregion |
| Argentina | South America | 4 | Country |
| Australia | Asia Pacific | 4 | Country |
| Pakistan | Asia Pacific | 4 | Country |
| South Africa | Africa | 4 | Country |
| Tunisia | Africa | 4 | Country |
| Uruguay | South America | 4 | Country |
+-------------------------------------------------------+
Here , regions are of 4 levels
All region
Region
Sub Region
Country
they have 0,1,2 and 3 ancestors,such as a country has subregion,region and allregion as ancestors ,suppose we give "Uruguay" ,then output will be South America, America , All Region.
Now, I need a query for this table,which will retrieve all ancestors for a given "child"
Your best bet is a recursive CTE:
With recRegions AS
(
/*Recursive Seed*/
SELECT
Child,
Parent,
Level,
0 as Depth,
CAST(Child as VARCHAR(5000)) as Path
FROM
Regions
WHERE
Child=<WhateverChildYouAreWanting>
UNION ALL
/*Recursive Term*/
SELECT
Regions.Child,
Regions.Parent,
Region.Level,
recRegions.Depth + 1,
recRegions.Path || '>' || Region.Child
FROM
recRegions
INNER JOIN Regions on
recRegions.parent = Regions.Child
Where recRegions.Depth < 10
)
Select Parent as Ancestors FROM recRegions;
Recursive queries can be a little tricky to wrap your head around at first, but if you break up the pieces of it, it makes sense:
Recursive Seed - This is the part where we get the first term we are after. In your case, we just want the record where the Child is the country you are wanting to query.
Recursive Term - This is the part where the query refers back to itself. It joins the recursive CTE recRegions to your Region table, connecting the child to the parent. The DB will hit this recursive term until no more records come back, which means we've climbed all the way up your hierarchy.
The final select statement just pulls back the records from your recursive query. You wanted all the ancestors, so that would be all of the Parent field records.
Generally when you see a table with a layout child | parent | attributes | of | that | relationship you can turn to the super powerful recursive CTE to make quick sense out of it all.
As #dnoeth mentioned in your Q's comments, you could also join Regions table to itself 4 times since your hierarchy seems to be only 4 deep. A recursive query doesn't care about depth though, so if you add more depth to your hierarchy, you won't have to edit your SQL to pull the ancestors.
Updated to add a "Depth" field to track recursions and stop after 10. Also added a "Path" field to track the hierarchy as it's built up from the Child. If you have an issue with your hierarchy cycling (a child reporting to a parent that reports to the child causing an endless loop) then you can use the following SQL statement instead of the SELECT parent FROM version above:
SELECT * FROM recRegions;
Now you will see Path and Depth of each node of your hierarchy so you can fix your data, or the recursive CTE to avoid the cycling.

Select one of each

How can I get all the countries from the DB, from this table:
city | country | info
Jerusalem | Israel | Capital
Tel Aviv | Israel |
New York | USA | Biggest
Washington DC | USA | Capital
Berlin | Germany | Capital
How can I get, using SQL, the countries only: Israel, USA, Germany?
Which database server are you using?
Assuming that the top row is the column name and you are using MySQL then you should be able to just do
"SELECT distinct(country) FROM <table-name>;"
This is probably in the documentation for the database software that you are using.