Retrieve an overview of all countries that have at least one city, how many cities they have. and the average population of these cities - mysql

another day and another mysql problem, ive been scratching my head with this question for quite some while now.
My task is through a database called "world" is to retrive and overview of countries with atleast one city, how many cities they have and the average population of these cities. i would also like to sort the average population by using " (AS AverageCityPopulation)" and the number of cities with "(AS NumberOfCities)".
ive just started to learn about join, left join and right join aswell and i am pretty certain that i have to use one of those 3 to complete the task. im still trying to find a helpful way to memorize when to use those 3 (if you have a tip please leave it down below).
anyways, the data should be sorted like this i feel like
countrycode
countryname
First
row
Second
row
cityname
citycountrycode
First
row
Second
row
averagecitypop
numberofcities
First
row
Second
row
of course the data should be displayed sideways but it is a bit hard to make it work in stackoverflow. anyways, i have tried with multiple queries for now, but still havent found the answer. the closest i got to was the entire avg population of a city in Aruba
my current query is:
SELECT
country.name,
country.code,
city.name,
AVG(city.population) AS averageCityPop,
city.countrycode
FROM
world.city
right JOIN
world.country ON city.CountryCode = country.code
where city.CountryCode > 1
again i am relativly new, so any thesis or curriculum is appriciated as answers in this post and answers to my question, if you also know any good youtube channels or forums where its helpful to learn mysql it would be great!
thanks for any helpful answers <3
here are a few screenshots about the two tables im trying to connect
world.city
world.country

Note that the database I use is MySQL sample database - World.
For beginners: both tables have primary keys (for table country, it is 'code', for table city, it is 'id'), so it's enough to use inner joins.
SELECT co.code AS country_code,
co.name AS country_name,
COUNT(*) AS num_cities,
AVG(ci.population) AS avg_city_pop
FROM country co INNER JOIN city ci ON (co.code = ci.countrycode)
GROUP BY co.code;
Or if you want to show the name of each city:
SELECT co.code AS country_code,
co.name AS country_name,
ci.name AS city_name,
COUNT(*) OVER w AS num_cities,
AVG(ci.population) OVER w AS avg_city_pop
FROM country co INNER JOIN city ci ON (co.code = ci.countrycode)
WINDOW w AS (PARTITION BY co.code);

Related

Have to enter mySQL criteria twice?

Say I have two tables:
Table: customers
Fields: customer_id, first_name, last_name
Table: customer_cars
Fields: car_id, customer_id, car_brand, car_active
Say I am trying to write a query that shows all customers with a first name of "Karl," and the brands of the ** active ** cars they have. Not all customers will have an active car. Some cars are active, some are inactive.
Please keep in mind that this is a representative example that I just made up, for sake of clarity and simplicity. Please don't reply with questions about why we would do it this way, that I could use table aliases, how it's possible to have an inactive car, or that my field names could be better written. It's a fake example that is intended be very simple in order to illustrate the point. It has a structure and issue that I encounter all the time.
It seems like this would be best done with a LEFT JOIN and subquery.
SELECT
customer_id,
first_name,
last_name,
car_brand
FROM
customers
LEFT JOIN
(SELECT
customer_id,
car_brand
FROM
customer_cars
INNER JOIN customers ON customer_cars.customer_id = customers.customer_id
WHERE
first_name = 'Karl' AND
customer_cars.car_active = '1') car_query ON customers.customer_id = car_query.customer_id
WHERE
first_name = 'Karl'
The results might look like this:
first_name last_name car_brand
Karl Johnson Dodge
Karl Johnson Jeep
Karl Smith NULL
Karl Davis Chrysler
Notice the duplication of 'Karl' in both WHERE clauses, and the INNER JOIN in the subquery that is the same table in the outer query. My understanding of mySQL is that this duplication is necessary because it processes the subquery first before processing the outer query. Therefore, the subquery must be properly limited so it doesn't scan all records, then it tries to match on the resulting records.
I am aware that removing the car_active = '1' condition would change things, but this is a requirement.
I am wondering if a query like this can be done in a different way that only causes the criteria and joins to be entered once. Is there a recommended way to prioritize the outer query first, then match to the inner one?
I am aware that two different queries could be written (find all records with Karl, then do another that finds matching cars). However, this would cause multiple connections to the database (one for every record returned) and would be very taxing and inefficient.
I am also aware of correlating subqueries, but from my understanding and experience, this is for returning one field per customer (e.g., an aggregate field such as how much money Karl spent) within the fieldset. I am looking for a similar approach as this, but where one customer could be matched to multiple other records like in the sample output above.
In your response, if you have a recommended query structure that solves this problem, it would be really helpful if you could write a clear example instead of just describing it. I really appreciate your time!
First, is a simple and straight query not enough?
Say I am trying to write a query that shows all customers with a first
name of "Karl," and the brands of the ** active ** cars they have. Not
all customers will have an active car. Some cars are active, some are
inactive.
Following this requirement, I can just do something like:
SELECT C.first_name
, C.last_name
, CC.car_brand
FROM customers C
LEFT JOIN cutomer_cars CC ON CC.customer_id = C.customer_id
AND car_active = 1
WHERE C.first_name = 'Karl'
Take a look at the SQL Fiddle sample.

How to join employees to orders?

I am simply not understanding joins very well at all!
My exercise calls for:
Display the employee’s last name and first name, city, country and ship city for orders where the employees live in the city where the order is delivered.
I have started my answer thus far here:
SELECT
Employees.LastName,
Employees.FirstName,
Employees.City,
Employees.Country,
Orders.ShipCity
FROM
Employees,
Orders
But then I realise that I am stuck and do not know where to go from here. Do I need to find a table that has common entries in both in order to find primary keys and foreign keys? What is my solution here?
For reference, here is an image of where I am taking data from for my solutions:
SELECT
Employees.LastName,
Employees.FirstName,
Employees.City,
Employees.Country,
Orders.ShipCity
FROM
Employees inner join Orders
on Employees.EmployeeID= Orders.EmployeeID;

Tricky intersection in Relational Algebra

Hi there for my exam revision i had picked up the following sample question for relational algebra:
employee (+person_name, street, city)
works (+person_name, company_name, salary)
company (+company_name, city)
manages (+person_name, manager_name)
+ indicate the underlined primary keys
Find the names of all employees who live in the same city and on the same street as their managers
MY solution
JOIN manages and employee (OVER person_name) GIVING T1
JOIN manages and employee (OVER manager_name) GIVING T2
PROJECT T1 over person_name, street, city GIVING T3
PROJECT T2 over street, city GIVING T4
T3 intersect T4 GIVING T5
PROJECT T5 over person_name GIVING RESULT
This was my solution until I had found out about that the intersection has to be union-compatible (number of columns matching and their headings)
Since then I couldn’t really find a solution to this problem because if I do the following change to line-3
PROJECT T1 over street, city GIVING T3
then I will never have the opportunity to link the result of intersection back to person_name.
On the other hand when I would make the following change to line-4:
PROJECT T2 over person_name, street, city GIVING T4
Then upon the intersection I would never get a person who has any other manager than himself.
I would appreciate any hints given, perhaps this online sample i picked up is quite ambiguous.
Another way to phrase the question: for every manager+person pair, find those for which the related city+state are the same for both people. You almost did that:
JOIN manages AND employee (OVER person_name) GIVING T1
JOIN T1 AND employee (OVER manager_name, street, city) GIVING T2
PROJECT T2 OVER person_name, manager_name, street, city GIVING RESULT
The problem statement does not require the names to returned in a single column, and this answer provides a useful result. If need be, you could repeat the above query, taking the union of two projections: one of person_name and the other of manager_name.
Just one thing: many managers would object to one column named "person" and the other "manager" because almost every manager -- your experience notwithstanding, perhaps? -- considers himself a person. More acceptable pairs might be manager/worker, lord/serf, master/slave, etc.

MySQL Struggling with join / selection criteria

I am hoping someone will be able to point me in the right direction.
I have a COUNTRY table, containing a primary ID, and country name (e.g. France)
I have a second table PAYMETHODS_COUNTRIES, which contains a primary id, payment method id (e.g. 4) and country id
I am trying to output a list of checkboxes on each payment method, to control which countries (or users in which countries) have access to those local payment methods.
I have the checkbox list working okay, and the processing of the form is also working. The last remaining thing, is to output the list of country checkboxes with selected countries (e.g. this payment method is available in France and Germany) shown as CHECKED.
So what I am trying to do is to output a complete list of all countries
SELECT id, name FROM country ORDER BY id ASC
And then include the PAYMETHODS_COUNTRIES table something like this (not working of course)
SELECT country.id AS countryid,
country.name AS countryname,
paymethods_countries.id AS methodid
FROM country LEFT JOIN `paymethods_countries`
ON country.id = paymethods_countries.country
WHERE paymethods_countries.paymethod = 10
This example query only shows rows that actually have a record in PAYMETHODS_COUNTRIES, not what I am after.
What I was hoping for, is a complete list of country.id + country.name, with a final field (the ID of the PAYMETHODS_COUNTRIES table) - which should be null where there is no link. That way I should be able to check for null or otherwise, and output CHECKED for those rows.
Hopefully that makes sense - or if anyone knows a better way to achieve what I am doing, your thoughts are appreciated.
While the other answer here will produce the desired result, semantically it is more correct to move the condition from the WHERE clause to the ON clause:
SELECT country.id AS countryid,
country.name AS countryname,
paymethods_countries.id AS methodid
FROM country
LEFT JOIN `paymethods_countries`
ON country.id = paymethods_countries.country and paymethods_countries.paymethod = 10
This eliminates the need to add the extra OR condition in the WHERE clause.
It's only coming back with rows that have record in PAYMETHODS_COUNTRIES because of your WHERE clause since you say the paymethod must = 10. Add an OR to let it be null too:
SELECT country.id AS countryid,
country.name AS countryname,
paymethods_countries.id AS methodid
FROM country
LEFT JOIN `paymethods_countries`
ON country.id = paymethods_countries.country
WHERE paymethods_countries.paymethod = 10
OR paymethods_countries.paymethod IS NULL

How do I make sure a value is not returned if it is connected to multiple values, one which is undesired?

My question isn't perfectly phrased, but basically here's the question.
I have two columns in a table "works". One column is a list of last names, and the other is a list of cities where the people live.
I don't want my query to return anybody who lives in Chicago.
My current code is:
select lives.last_name
from lives
where lives.city <> "Chicago";
But some people live in both Chicago and somewhere else, say Miami, and they still show up. How do I only return people who don't live in Chicago without using group or count functions (not that far yet)
Thanks
you can use something like this with NOT EXISTS:
select a.last_name
from lives a
where not exists (select last_name
from lives b
where a.last_name = b.last_name
and city = 'chicago')
See SQL Fiddle with Demo
Try using NOT IN
SELECT last_name
FROM lives
WHERE last_name NOT IN
(
SELECT last_Name
FROM lives
WHERE city = 'Chicago'
)
You may group on last name selecting all cities and then filtering records not containign Chicago:
Select Last_name, City
from lives
group by Last_name
having group_concat(city) not Regexp 'Chicago'