Task: From MySQL standard database "world" select all the cities with population more then a million, limiting number of countries to 10.
If I do this:
SELECT country.Name, city.Name, city.Population
FROM country
INNER JOIN city
ON country.Code = city.CountryCode
WHERE city.Population >= 1000000
LIMIT 10;
it produces this:
+-------------+--------------+------------+
| Name | Name | Population |
+-------------+--------------+------------+
| Afghanistan | Kabul | 1780000 |
| Algeria | Alger | 2168000 |
| Angola | Luanda | 2022000 |
| Argentina | Buenos Aires | 2982146 |
| Argentina | La Matanza | 1266461 |
| Argentina | Córdoba | 1157507 |
| Armenia | Yerevan | 1248700 |
| Australia | Sydney | 3276207 |
| Australia | Melbourne | 2865329 |
| Australia | Brisbane | 1291117 |
+-------------+--------------+------------+
Argentina, for example, is repeated 3 times. I need to limit number of countries to 10, not number of rows.
GROUP BY won't do since I need all the cities, I don't need them grouped.
The expected result would be this:
+--------------------+----------------------------+------------+
| Name | Name | Population |
+--------------------+----------------------------+------------+
| Afghanistan | Kabul | 1780000 |
| Algeria | Alger | 2168000 |
| Angola | Luanda | 2022000 |
| Argentina | Buenos Aires | 2982146 |
| Argentina | La Matanza | 1266461 |
| Argentina | Córdoba | 1157507 |
| Armenia | Yerevan | 1248700 |
| Australia | Sydney | 3276207 |
| Australia | Melbourne | 2865329 |
| Australia | Brisbane | 1291117 |
| Australia | Perth | 1096829 |
| Azerbaijan | Baku | 1787800 |
| Bangladesh | Dhaka | 3612850 |
| Bangladesh | Chittagong | 1392860 |
| Brazil | São Paulo | 9968485 |
| Brazil | Rio de Janeiro | 5598953 |
| Brazil | Salvador | 2302832 |
| Brazil | Belo Horizonte | 2139125 |
| Brazil | Fortaleza | 2097757 |
| Brazil | BrasÃlia | 1969868 |
| Brazil | Curitiba | 1584232 |
| Brazil | Recife | 1378087 |
| Brazil | Porto Alegre | 1314032 |
| Brazil | Manaus | 1255049 |
| Brazil | Belém | 1186926 |
| Brazil | Guarulhos | 1095874 |
| Brazil | Goiânia | 1056330 |
| United Kingdom | London | 7285000 |
| United Kingdom | Birmingham | 1013000 |
+--------------------+----------------------------+------------+
As you can see, number of countries is 10, no matter how many cities.
Of course, my database is different. I used the "world" for simplicity, generality, and availability.
In the real db I also do some filtering on "country".
I Hope this will work
SELECT country.Name, city.Name, city.Population
FROM country
LEFT JOIN city
ON country.Code = city.CountryCode
WHERE city.Population >= 1000000
AND city.id IN (SELECT id FROM city WHERE city.Population >= 1000000 AND country.Code = city.CountryCode LIMIT 10);
//So sorry try this query
SELECT country.Name, city.Name, city.Population
FROM country
LEFT JOIN city
ON country.Code = city.CountryCode
WHERE city.Population >= 1000000 and country.Code IN(SELECT distinct CountryCode FROM city WHERE Population >= 1000000 LIMIT 10);
Generate temporary row level id's for each of the unique country in a sub query and select rows from it applying a where condition like where temp_row_num is <= 10.
Example:
select
/*uq_country_row_num, */
country_name, city_name, population
from (
select country_name, city_name, population
, case when #pn=(#cn:=country_name)
then #r:=(#r + 1)
else #r:=1
end as uq_country_row_num
, #pn:=#cn as temp_cn
from population,
(select #pn:='', #cn:='', #r:=0) country_rown_nums
-- use order by country_name if the result set is un-ordered
order by
country_name
) all_results
where
uq_country_row_num <= 10
;
You can un comment the variable /*uq_country_row_num, */, if you want them as part of the output.
Example # SQL Fiddle
Edit:
From the comments:
You mean 10 rows per country or all records from max 10 countries?
Exactly, "all records from max 10 countries".
select
country_id,
country_name, city_name, population
from (
select
country_name, city_name, population
, case when #pn=(#cn:=country_name)
then #country_id:=#country_id
else #country_id:=(#country_id + 1)
end as country_id
, #pn:=#cn as temp_cn
from population,
(select #pn:='', #cn:='', #row_num:=0, #country_id:=0) country_row_nums
order by country_name
) all_results
where
country_id <= 10
;
Example 2 # SQL Fiddle
Try this once
SELECT city.Name, city.Population, newcountry.name
FROM city
INNER JOIN
(select distinct Name,Code
from country limit 10) newcountry
ON newcountry.Code = city.CountryCode
WHERE city.Population >= 1000000;
None of the answers worked. So I had to use the following request:
SELECT co.Code, co.Name, ci.Name, ci.Population
FROM city ci
INNER JOIN (
SELECT DISTINCT co.Code, co.Name
FROM country co
INNER JOIN city ci
ON ci.countryCode = co.Code
WHERE ci.Population > 1000000
ORDER BY co.Name
LIMIT 10
) co
ON ci.countryCode = co.Code
WHERE ci.Population > 1000000;
It seems rather clumsy and not efficient to me (notice the repetition of the condition), but for lack of a better way, this is the only solution.
Thanks to everyone for your answers anyway.
Related
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 like to know how many users are from what city and Boroughs/Neighborhoods within that City. So I am looking for a query that will output the count results of these two. I have a table with user data, without the specific City and Boroughs/Neighborhoods, but with a postal code from each user. This can be used to match on the table postcodes, witch contains the City and Neighborhoods data.
Table Users
+----+--------------------+--------------+
| ID | User | Postcode |
+----+--------------------+--------------+
| 10 | John Doe | 1100—99-AB |
| 11 | Shara Lee | 1201—34-CD |
| 12 | Patrick Star | 1100—99-AB |
| 13 | Oswald Harvey | 1100—99-AB |
| 14 | Samuel Jackson | 1401—34-TR |
| 15 | Richard Lionheart | 1744—39-AA |
| 16 | Shamanta Jones | 2334—95-AC |
| 17 | James Rooney | 1401—34-TR |
| 18 | Chandler Bing | 3334—23-AA |
| 19 | Jessica Burner | 2277—99-RA |
+----+--------------------+--------------+
Table Postcodes
+------------+--------------+-------------+
| Postcode | City | Borough |
+------------+--------------+-------------+
| 1100—99-AB | New York | Manhattan |
| 1201—34-CD | New York | Manhattan |
| 1401—34-TR | New York | Bronx |
| 1744—39-AA | New York | Harlem |
| 2334—95-AC | Newark | |
| 6334—95-AC | Detroit | Greektown |
| 3334—23-AA | Philadelphia | Penn Center |
| 2277—99-RA | Newark | |
+------------+--------------+-------------+
Result I am after
+--------------+--------------+-------------+---------------+
| City | Total_City | Borough | Total_Borough |
+--------------+--------------+-------------+---------------+
| New York | 7 | Manhattan | 4 |
| New York | 7 | Bronx | 2 |
| New York | 7 | Harlem | 1 |
| Newark | 2 | | 2 |
| Philadelphia | 1 | Penn Center | 1 |
+--------------+--------------+-------------+---------------+
This is how far I got with my query. This does correctly count the number Boroughs/Neighborhoods, but unfortunately it does not show the total city count.
SELECT City, Borough, COUNT(City) AS Total_City, COUNT(Borough) AS Total_Borough,
FROM `users` u
LEFT JOIN `postcodes` p ON p.postcode = u.postcode
GROUP BY City, Borough
See my example on http://rextester.com/DFRV4183
Here's one way...
SELECT a.*, b.total total_city
FROM
( SELECT p.city
, p.borough
, COUNT(*) total
FROM postcodes p
JOIN myusers u
ON u.postcode = p.postcode
GROUP
BY city
, borough
) a
JOIN
( SELECT p.city
, COUNT(*) total
FROM postcodes p
JOIN myusers u
ON u.postcode = p.postcode
GROUP
BY city
) b
ON b.city = a.city;
You can also do something like this, but I'm not convinced that the result is easier to understand...
SELECT city
, borough
, COUNT(*) total
FROM postcodes p
JOIN myusers u
ON u.postcode = p.postcode
GROUP
BY city
, borough WITH ROLLUP;
Finally, you can use variables. Maybe I'll post an answer along those lines later... if someone doesn't beat me to it.
SELECT b.City, Borough, Total_City, Total_Borough from
(SELECT City, coalesce(COUNT(User),0) AS Total_City
FROM `Users` u
right JOIN `Postcodes` p ON p.postcode = u.postcode
GROUP BY City ) as c
inner join
(
SELECT City, coalesce(Borough,'Undefined')as Borough,coalesce( COUNT(User),0) AS Total_Borough, p.postcode
FROM `users` u
right JOIN `postcodes` p ON p.postcode = u.postcode
GROUP BY City, Borough
) as b
on b.City=c.City
I have three tables with the following data:
countries
+-----+----------+
| id | name |
+-----+----------+
| 1 | country1 |
| 7 | country2 |
+-----+----------+
states
+-----+----------+------------+
| id | name | country_id |
+-----+----------+------------+
| 3 | state1 | 1 |
| 9 | state2 | 7 |
| 11 | state3 | 1 |
| 17 | state4 | 1 |
+-----+----------+------------+
cities
+-----+----------+------------+
| id | name | state_id |
+-----+----------+------------+
| 5 | city1 | 3 |
| 6 | city2 | 9 |
| 22 | city3 | 9 |
| 24 | city4 | 17 |
| 25 | city5 | 11 |
| 26 | city6 | 11 |
+-----+----------+------------+
I’m trying to select all data so that I can generate the following output:
+-----+---------------------------+--------+-------+
| id | table_name | country | state | city |
+-----+---------------------------+--------+-------+
| 1 | countries | country1 | | |
| 3 | states | country1 | state1 | |
| 5 | cities | country1 | state1 | city1 |
| 11 | states | country1 | state3 | |
| 25 | cities | country1 | state3 | city5 |
| 26 | cities | country1 | state3 | city6 |
| 17 | states | country1 | state4 | |
| 24 | cities | country1 | state4 | city4 |
| 7 | countries | country2 | | |
| 9 | states | country2 | state2 | |
| 5 | cities | country2 | state2 | city2 |
| 5 | cities | country2 | state2 | city3 |
+-----+---------------------------+--------+-------+
I know it’s challenging, but I was wondering if that is possible to generate such a result with a SELECT or can it only be done programmatically? Thanks!
You need these 3 SQL statements:
All Cities:
SELECT cit.id, 'cities', cont.name, st.name, cit.name
FROM countries cont
INNER JOIN states st ON cont.id = st.country_id
INNER JOIN join cities cit ON st.id = cit.state_id
All states:
SELECT stat.id, 'states', cont.name, st.name, ''
FROM countries cont
INNER JOIN states st ON cont.id = st.country_id
All countries;
SELECT cont.id, 'countries', cont.name, '', '' FROM countries cont
Then you can combine them all like
SELECT cit.id, 'cities', cont.name, st.name, cit.name
FROM countries cont
INNER JOIN states st ON cont.id = st.country_id
INNER JOIN join cities cit ON st.id = cit.state_id
UNION ALL
SELECT stat.id, 'states', cont.name, st.name, ''
FROM countries cont
INNER JOIN states st ON cont.id = st.country_id
UNION ALL
SELECT cont.id, 'countries', cont.name, '', '' FROM countries cont
You can use below query
SELECT C.id, C.name AS table_name, 'country1' AS country, S.name AS state, CI.city
FROM COUNTRIES C
FULL OUTER JOIN STATES S
ON (C.ID = S.ID)
FULL OUTER JOIN CITIES CI
ON (C.ID=CI.ID);
Or you can use
SELECT C.id, C.name AS table_name, 'country1' AS country, S.name AS state, CI.city
FROM COUNTRIES C, STATES S, CITIES CI
WHERE C.ID = (+)S.ID AND
C.ID=(+)CI.ID;
I'd written this before I saw #nos had already supplied a virtually identical answer. This version adds field aliases, sorts the data as per the OP's sample output and avoids a typo.
select
c.id,
'countries' as table_name,
c.name as country,
'' as state,
'' as city
from countries c
union
select
s.id,
'states' as table_name,
c.name as country,
s.name as state,
'' as city
from countries c
JOIN states s ON c.id = s.country_id
union
select
ci.id,
'cities' as table_name,
c.name as country,
s.name as state,
ci.name as city
from countries c
JOIN states s ON c.id = s.country_id
JOIN cities ci ON s.id = ci.state_id
order by country, state, city
My question is surely banal but i can't set up an sql query that allows me to make a list of top 3 countries for a sport-event summary table.
I explain me better: in a sport event i have a lot of athletes from different countries and i need to produce a summary table showing countries that won more medals.
Here is an example:
--------------------------------------------
|id | name | activity | country |
--------------------------------------------
| 1 | John | 100m | USA |
| 2 | Andy | 200m | CANADA |
| 3 | Frank | 400m | USA |
| 4 | Ian | 400m | GERMANY |
| 5 | Anthony | 100m | USA |
| 6 | Eric | 400m | CANADA |
| 7 | Mike | 200m | UK |
| 8 | Dave | 200m | GERMANY |
| 9 | Richard | 100m | USA |
| 10| Max | 100m | USA |
| 11| Randy | 100m | USA |
| 12| Maurice | 400m | CANADA |
| 13| Col | 100m | UK |
| 14| Jim | 400m | USA |
| 15| Adam | 200m | BRAZIL |
| 16| Ricky | 100m | UK |
| 17| Emily | 400m | USA |
| 18| Serge | 200m | UK |
| 19| Alex | 400m | FRANCE |
| 20| Enamuel | 100m | USA |
--------------------------------------------
The summary table i wish to obtain is the following:
Top 3 countries
--------------------------------------
| position | country | medals |
--------------------------------------
| 1 | USA | 9 |
| 2 | UK | 4 |
| 3 | CANADA | 3 |
--------------------------------------
How can build the qsl query?
Thanks in advance for your kind answer.
Mattew
Without the position column, this is quite easy. Just do the following
SELECT Country,COUNT(*) AS medals
FROM MyTable
GROUP BY Country
ORDER BY COUNT(*) DESC
LIMIT 3;
There is some more complicated code for getting the "position" column out, but unless you need it, it probably isn't necessary, and you can just get those numbers using a counter on the processing code. If you're interested, the code would be something like this.
SELECT #rownum:=#rownum+1 AS Position,Country,Medals FROM
(
SELECT Country,COUNT(*) AS medals
FROM Medals
GROUP BY Country
ORDER BY COUNT(*) DESC
LIMIT 3
) AS Stats, (SELECT #rownum:=0) RowNum;
The above query has been tested and appears to be working as you need it to be.
CREATE TABLE IF NOT EXISTS top_three_countries
(position INT NOT NULL AUTO_INCREMENT, country VARCHAR(30), medals INT);
TRUNCATE TABLE top_three_countries;
INSERT INTO top_three_countries (country, medals)
SELECT country, count(*) total
FROM medal
GROUP BY country
ORDER BY total DESC
LIMIT 3;
This will produce a summary table (top_three_countries) as you describe.
It's much simpler without the rank, if you can add that in your programs' logic instead:
SELECT `country`, COUNT(*) total
FROM medal
GROUP BY country
ORDER BY total DESC
LIMIT 3
Looks like Kibbee beat me too it but for a guaranteed GROUP BY compatible query you can wrap the above in a SELECT of its own:
SELECT #n:=#n+1 AS rank, country, total
FROM
(
SELECT `country`, COUNT(*) total
FROM medal
GROUP BY country
ORDER BY total DESC
LIMIT 3
) t1,
(SELECT #n:=0) t2
Lets say I have the following tables:
Countries
---------------------------
| ID | Country Name |
---------------------------
| 1 | Greece |
| 2 | Italy |
| 3 | Spain |
---------------------------
Cities
---------------------------
| ID | City |
---------------------------
| 1 | Athens |
| 2 | Patra |
| 3 | Rome |
| 4 | Venice |
---------------------------
Countries & Cities
--------------------
| ID | Cntr | City |
--------------------
| 1 | 1 | 2 |
| 2 | 1 | 1 |
| 3 | 2 | 3 |
--------------------
Now, How can I run a MySQL query that will return the name of the countries and the total cities based on table "Countries & Cities" ?
In example to return:
---------------------------
| Cities | Country |
---------------------------
| 2 | Greece |
| 1 | Italy |
---------------------------
Try this out:
SELECT COUNT(cs.City) as Cities, cn.name as Country
FROM countries cn
INNER JOIN country_city cs ON cs.Cntr = cn.id
GROUP BY cn.name
OUTPUT:
2 | Greece
1 | Italy
There is only one JOIN is needed
SELECT `c`.`name`, COUNT(`c`.id)
FROM `countries_cities` AS `cc`
JOIN `countries` AS `c`
ON `c`.id = `cc`.country_id
GROUP BY `cc`.country_id
SELECT cn.Name, COUNT(*)
FROM CountriesAndCities cc
JOIN Countries cn ON (cn.ID = cc.Cntr)
GROUP BY cn.Name
You only need to group the countries in the countries & cities table:
SELECT COUNT(1), c.Name
FROM [countriesAndCities] cnc
INNER JOIN [country] c ON cnc.cnt = c.id
GROUP BY c.Name