I've got data with geographical locations. That may be cities, provinces, states, countries, continents, or whatever kind of location. A location can be part of an other location, like a State is part of a Country (like the US), but The Netherlands has provinces. Cities can be in a Province but they aren't necessarily part of a province. Partly because some cities are city-state(-like) (i.e. Luxembourg), but in my case it's not even relevant in what province a city is, if there's only one city from a country in my locations list.
This this very simple example:
| types |
|-----------|
| Continent |
| Country |
| Province |
| City |
Locations
| id | name | type | parent |
|----|---------------|-----------|--------|
| 1 | Europe | Continent | NULL |
| 2 | Netherlands | Country | 1 |
| 3 | Noord-Holland | Province | 2 |
| 4 | Amsterdam | City | 3 |
| 5 | Haarlem | City | 3 |
| 6 | Luxembourg | City | 1 |
For every location I want to know their 'geographical parent' (if exist). So the expected outcome is this:
| id | name | type | Continent | Country | Province |
|----|---------------|-----------|-----------|-------------|---------------|
| 1 | Europe | Continent | | | |
| 2 | Netherlands | Country | Europe | | |
| 3 | Noord-Holland | Province | Europe | Netherlands | |
| 4 | Amsterdam | City | Europe | Netherlands | Noord-Holland |
| 5 | Haarlem | City | Europe | Netherlands | Noord-Holland |
| 6 | Luxembourg | City | Europe | | |
How can I get all types as a column for my Locations table? I've tried to use subqueries, but I'm completely stuck because of the recursiveness: Amsterdam is not part of a country (but it's Province is), while Luxembourg is part of a country (without a Province).
How can I get the expected output?
Surely there is a better solution but at the moment I wrote this:
SELECT Locations.id,
Locations.name,
Locations.type,
IF(l3.name IS NULL, IF(l2.name IS NULL, IFNULL(l1.name, ''), l2.name), l3.name) as Continent,
IF(l3.name IS NULL, IF(l2.name IS NULL, '', l1.name), l2.name) as Country,
IF(l3.name IS NULL, '', l1.name) as Province
FROM Locations
LEFT join Locations l1 on Locations.parent = l1.id
LEFT join Locations l2 on l1.parent = l2.id
LEFT join Locations l3 on l2.parent = l3.id
ORDER BY Locations.id
DEMO
Related
I was trying to learn non-equi joins when I encountered this problem. I have a table pops:
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| country | varchar(100) | YES | | NULL | |
| continent | varchar(100) | YES | | NULL | |
| population | bigint(20) | YES | | NULL | |
I was trying to find countries with their population in the vicinity of say, 100.
select distinct
p1.country,
p2.country,
p1.population,
p2.population
from pops p1
inner join pops p2
on p1.population between p2.population - 100 and p2.population + 100
and p1.country <> p2.country
where p2.country <> p1.country
output I got was:
+------------+------------+------------+------------+
| country | country | population | population |
+------------+------------+------------+------------+
| pakistan | india | 99988 | 99999 |
| china | india | 99990 | 99999 |
| bangladesh | japan | 999 | 999 |
| india | pakistan | 99999 | 99988 |
| china | pakistan | 99990 | 99988 |
| japan | bangladesh | 999 | 999 |
| india | china | 99999 | 99990 |
| pakistan | china | 99988 | 99990 |
+------------+------------+------------+------------+
as we can see, I am getting pairs of (india, pakistan) as well as (pakistan, india), which is data-wise the same thing. Is it possible to eliminate one of the records from the pair?
You could decide to always have the lexographically first (or last, for argument's sake) country on the p1 side - use < (or >) instead of <>.
Also, note that your where clause is redundant, since you already have this condition in the on clause of the join:
select p1.country,
p2.country,
p1.population,
p2.population
from pops p1
inner join pops p2
on p1.population between p2.population - 100 and p2.population + 100 and
p1.country < p2.country
-- Here ------------------^
just change the join condition of p1.country <> p2.country to p1.country < p2.country
I have this locations table:
+----+-----------+------------+----------+
| id | country | state | city |
+----+-----------+------------+----------+
| 1 | US | Georgia | Atlanta |
| 2 | US | California | |
| 3 | US | | |
| 4 | Canada | Ontario | |
| 5 | Canada | Manitoba | Winnipeg |
| 6 | Canada | | |
I want to create a query but could not build my ORDER BY properly. This is the result that I want:
+----+-----------+------------+----------+
| id | country | state | city |
+----+-----------+------------+----------+
| 6 | Canada | | |
| 3 | US | | |
| 4 | Canada | Ontario | |
| 2 | US | California | |
| 5 | Canada | Manitoba | Winnipeg |
| 1 | US | Georgia | Atlanta |
Basically, this is the priority that I want to follow:
Country listing. Alphabetical order.
State listing. Alphabetical order.
City listing. Alphabetical order.
This query does not seem to account for empty columns (I am not using NULLs in my locations table):
SELECT * FROM locations
ORDER BY
country,state,city
Try this ORDER BY clause:
SELECT *
FROM locations
ORDER BY
CASE WHEN state = '' AND city = '' THEN 0
WHEN city = '' THEN 1
ELSE 2 END,
country,
state,
city;
This sorting logic places first those records which are missing both state and city. Next follows records missing city only, followed last by records having non empty data for all three fields.
For versions pre 8.0...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,country VARCHAR(20) NOT NULL
,state VARCHAR(20) NULL
,city VARCHAR(20) NULL
);
INSERT INTO my_table VALUES
(1,'US','Georgia','Atlanta'),
(2,'US','California',NULL),
(3,'US',NULL,NULL),
(4,'Canada','Ontario',NULL),
(5,'Canada','Manitoba','Winnipeg'),
(6,'Canada',NULL,NULL);
SELECT id
, country
, state
, city
FROM
( SELECT x.*
, CASE WHEN #prev=country THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=country
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY country
, city
, state
) a
ORDER
BY i
, country;
+----+---------+------------+----------+
| id | country | state | city |
+----+---------+------------+----------+
| 6 | Canada | NULL | NULL |
| 3 | US | NULL | NULL |
| 4 | Canada | Ontario | NULL |
| 2 | US | California | NULL |
| 5 | Canada | Manitoba | Winnipeg |
| 1 | US | Georgia | Atlanta |
+----+---------+------------+----------+
select *
from locations
order by
state <> '',
city <> '',
country,
state,
city
db-fiddle
Note that in MySQL a boolean expression returns 0 (for FALSE) or 1 (for TRUE). That means for an empty state string state <> '' will return 0 and thus ordered first.
I am trying to join 3 tables but it looks like I am doing something wrong. Also I am not sure if INNER JOIN is a good idea here because there might be some missing corresponding rows in the other tables, but I would still like them to show up as NULL, then maybe LEFT JOIN would be better, but again, I'm doing something wrong.
A quick summary of the table responses:
SELECT geonameid, name, iso_alpha2, admin1_code
FROM maxmind_cities1000
WHERE name LIKE 'tron%' LIMIT 20;
+-----------+------------------------+------------+-------------+
| geonameid | name | iso_alpha2 | admin1_code |
+-----------+------------------------+------------+-------------+
| 1605268 | Tron | TH | 10 |
| 8949073 | Tronca | IT | 03 |
| 3107444 | Tronchón | ES | 52 |
| 8859151 | Tronconal | MX | 21 |
| 2821000 | Tröndel | DE | 10 |
| 3133880 | Trondheim | NO | 16 |
| 1252408 | Trongsa | BT | 21 |
| 2667264 | Trönninge | SE | 06 |
| 6535739 | Trontano | IT | 12 |
| 2971582 | Tronville-en-Barrois | FR | B2 |
| 3165134 | Tronzano Lago Maggiore | IT | 09 |
| 3165133 | Tronzano Vercellese | IT | 12 |
+-----------+------------------------+------------+-------------+
SELECT iso_alpha2, name
FROM maxmind_countryinfo
WHERE iso_alpha2 = 'NO';
+------------+--------+
| iso_alpha2 | name |
+------------+--------+
| NO | Norway |
+------------+--------+
SELECT code, name_local
FROM maxmind_admin1_codes_ascii
WHERE code = 'NO.16';
+-------+-----------------+
| code | name_local |
+-------+-----------------+
| NO.16 | Sør-Trøndelag |
+-------+-----------------+
So basically I am trying to join these three tables with this query, I have made a special case and said ON admin1.code = 'NO.16'
SELECT
city.geonameid as city_id,
city.name as city_name,
country.name as country_name,
admin1.name_local as admin1_code
FROM maxmind_cities1000 as city
INNER JOIN maxmind_countryinfo as country
ON city.iso_alpha2 = country.iso_alpha2
INNER JOIN maxmind_admin1_codes_ascii as admin1
ON admin1.code = 'NO.16'
WHERE city.name LIKE 'tron%' LIMIT 20;
but it gives me all the rows anyways instead of just Trondheim norway, so I am doing something wrong here. I tried switching to LEFT JOIN but get the same result set. I would like the city to show up in the result set even if there are no matching rows in maxmind_admin1_codes_ascii table, the admin code has the format iso_aplha2 '.' admin1_code
+---------+------------------------+--------------+-----------------+
| city_id | city_name | country_name | admin1_code |
+---------+------------------------+--------------+-----------------+
| 1605268 | Tron | Thailand | Sør-Trøndelag |
| 8949073 | Tronca | Italy | Sør-Trøndelag |
| 3107444 | Tronchón | Spain | Sør-Trøndelag |
| 8859151 | Tronconal | Mexico | Sør-Trøndelag |
| 2821000 | Tröndel | Germany | Sør-Trøndelag |
| 3133880 | Trondheim | Norway | Sør-Trøndelag |
| 1252408 | Trongsa | Bhutan | Sør-Trøndelag |
| 2667264 | Trönninge | Sweden | Sør-Trøndelag |
| 6535739 | Trontano | Italy | Sør-Trøndelag |
| 2971582 | Tronville-en-Barrois | France | Sør-Trøndelag |
| 3165134 | Tronzano Lago Maggiore | Italy | Sør-Trøndelag |
| 3165133 | Tronzano Vercellese | Italy | Sør-Trøndelag |
+---------+------------------------+--------------+-----------------+
This is my end result query but still don't understand why it gives me all results when I just want the special case 'NO.16'. And how should I structure my query if I want the cities to show regardless of there are no matching rows in the maxmind_admin1_codes_ascii table? This is what I have so far
SELECT
city.geonameid as city_id,
city.name as city_name,
country.name as country_name,
admin1.name_local as admin1_code
FROM maxmind_cities1000 as city
INNER JOIN maxmind_countryinfo as country
ON city.iso_alpha2 = country.iso_alpha2
INNER JOIN maxmind_admin1_codes_ascii as admin1
ON admin1.code = CONCAT(city.iso_alpha2, '.', city.admin1_code)
WHERE city.name LIKE 'tron%' LIMIT 20;
+---------+------------------------+--------------+--------------------+
| city_id | city_name | country_name | admin1_code |
+---------+------------------------+--------------+--------------------+
| 1605268 | Tron | Thailand | Uttaradit |
| 8949073 | Tronca | Italy | Calabria |
| 3107444 | Tronchón | Spain | Aragon |
| 8859151 | Tronconal | Mexico | Puebla |
| 2821000 | Tröndel | Germany | Schleswig-Holstein |
| 3133880 | Trondheim | Norway | Sør-Trøndelag |
| 1252408 | Trongsa | Bhutan | Tongsa |
| 2667264 | Trönninge | Sweden | Halland |
| 6535739 | Trontano | Italy | Piedmont |
| 2971582 | Tronville-en-Barrois | France | Lorraine |
| 3165134 | Tronzano Lago Maggiore | Italy | Lombardy |
| 3165133 | Tronzano Vercellese | Italy | Piedmont |
+---------+------------------------+--------------+--------------------+
This gives the result I want, but I don't think I am doing it right because the result was unexpected with the special case of 'NO.16'. Hope someone can help out!
Not totally sure if I understand what you are trying to do - but maybe this?
SELECT
city.geonameid as city_id,
city.name as city_name,
country.name as country_name,
admin1.name_local as admin1_code
FROM maxmind_cities1000 as city
INNER JOIN maxmind_countryinfo as country
ON city.iso_alpha2 = country.iso_alpha2
LEFT OUTER JOIN maxmind_admin1_codes_ascii as admin1
ON admin1.code = CONCAT(city.iso_alpha2, '.', city.admin1_code)
AND admin1.code = 'NO.16'
WHERE city.name LIKE 'tron%' LIMIT 20;
SELECT
city.geonameid as city_id,
city.name as city_name,
country.name as country_name,
admin1.name_local as admin1_code
FROM maxmind_cities1000 as city
INNER JOIN maxmind_countryinfo as country
ON city.iso_alpha2 = country.iso_alpha2
INNER JOIN maxmind_admin1_codes_ascii as admin1
ON admin1.code = city.admin1_code
WHERE city.name LIKE 'tron%' LIMIT 20 AND city.admin1_code = 'NO.16';
Hi I want to display the country(Land in german) which haves the most museums.
My table looks like:
+-----------+----------------------+-------------------+--------------------------+---------------+
| MuseumsNR | Name | Stadt | Land | Hauptstadt |
+-----------+----------------------+-------------------+--------------------------+---------------+
| 1 | Museum of Modern Art | New York | United States of America | Washington DC |
| 2 | Kunstmuseum | Bern | Schweiz | Bern |
| 3 | Musée Picasso | Paris | Frankreich | Paris |
| 4 | Städel | Frankfurt am Main | Deutschland | Berlin |
| 5 | Museum Ludwig | Köln | Deutschland | Berlin |
+-----------+----------------------+-------------------+--------------------------+---------------+
So my desired output should be:
+--------------------------+------+
| Land |Anzahl|
+--------------------------+------+
| Deutschland | 2 |
+--------------------------+------+
This is what i´ve tried:
SELECT Land, COUNT(Name) Name from Museum order by Name desc;
THe Output:
+--------------------------+------+
| Land | Name |
+--------------------------+------+
| United States of America | 5 |
+--------------------------+------+
Thanks in advance for your help!
SELECT Land, count(*) as Anzahl
FROM yourTable
GROUP BY Land
ORDER BY Anzahl
DESC LIMIT 1;`
Only tested in PostgreSQL, but should be close enough in MySQL.
Similar question here but this is slightly different...
I have two tables that I want to join:
location
---------------------------
| id | city | state_id |
---------------------------
| 1 | Denver | 6 |
| 2 | Phoenix | 2 |
| 3 | Seattle | NULL |
---------------------------
state
-------------------
| id | name |
-------------------
| 1 | Alabama |
| 2 | Alaska |
| 3 | Arizona |
| 4 | Arkansas |
| 5 | California |
| 6 | Colorado |
-------------------
SELECT
location.id,
location.city,
state.name
FROM
location
JOIN
state ON state.id = location.state_id;
However, in the case where location.state_id happens to be NULL (perhaps the person inputting the data forgot to select a state), the query would not return a result, but that doesn't mean the location doesn't exist.
How do I get around this problem and somehow display all the locations, even though the state_id might be NULL ?
Use a LEFT OUTER JOIN
SELECT
location.id,
location.city,
state.name
FROM
location
LEFT OUTER JOIN
state ON state.id = location.state_id;