Getting error in SQL query using INNER JOIN - mysql

I'm still learning the MySQl.
This is the relational DBMS :
CUSTOMER (CustID, CustName, AnnualRevenue)
TRUCK (TruckNumber, DriverName)
CITY (CityName, Population)
SHIPMENT (ShipmentNumber, CustID, Weight, Year, TruckNumber, CityName)
Now, I have to formulate for these two queries:
Total weight of shipments per year for each city.
Drivers who drove shipments to London but not Paris.
These are the queries i have came up with:
1.
select sum(s.weight), s.year , c.city
from shipment s, city c
INNER JOIN CITY
on s.CityName = c.CityName

You are mixing and old way to JOIN table (which you should avoid because the joining columns are not explicitly stated and it is confusing for others):
FROM shipment s, city c
You should group columns in the select that are not aggregated (year, city). Also it is better to use an alias for the aggregated column (AS total_weight)
select sum(s.weight) AS total_weight, s.year , c.city
from shipment s
INNER JOIN CITY as c
on s.CityName = c.CityName
GROUP BY s.year, c.city
Try to solve the second query and come back if you have a problem.

Related

SQL max is non-deterministic?

I have two tables: cities and states. States has columns for state codes and full name. Cities contains columns for population, state code, and the city name. My goal is to create a table of the city in each state with the highest population.
This is my solution which seems to work in a test, but I've been told that using max() is non-deterministic and I should use a window function instead.
SELECT
s.name,
c.name,
max(c.population)
FROM cities AS c
LEFT JOIN states AS s
ON c.state_code = s.code
GROUP BY s.name
ORDER BY s.name;
What is wrong with using max here, when would it give incorrect results?
In most databases your query would not even run, because you are selecting the non-aggregated column c.name without also using it in the GROUP BY clause.
For MySql, the code would run if ONLY_FULL_GROUP_BY mode is disabled, but still it would return wrong results because the query would pick a random city name out of all the cities of each state.
See the demo.
For SQLite, your query is correct!
SQLite's feature of bare columns, makes sure that the city name you get in the results is the one that has the max population.
This is non-standard, but it is documented.
The only problem here is that if there are 2 or more cities with the same max population you will get only one of them in the results.
See the demo.
You can find the city in each state with the max population and use it in a sub-query and join it with the tables.
Query
select s.name as state, c.name as city, c.population
from states s
join cities c
on c.state_code = s.code
join (
select state_code, max(population) as max_pop
from cities
group by state_code
) as p
on p.state_code = c.state_code
and p.max_pop = c.population;
create table states(code varchar(50),name varchar(50));
create table cities(code varchar(50),name varchar(50),population int, state_code varchar(50));
insert into states values('s01','state1');
insert into cities values('c01','city1',100,'s01');
insert into cities values('c02','city2',10,'s01');
Query:
with cte as
(
SELECT
s.name state_name,
c.name city_name,
c.population,
row_number()over(partition by s.name order by c.population desc)rn
FROM cities AS c
LEFT JOIN states AS s
ON c.state_code = s.code
)
select state_name, city_name, population from cte where rn=1
Output:
state_name
city_name
population
state1
city1
100
db<>fiddle here

Problems with subqueries using Sakila

Using the Sakila DB, i am trying to get the Country name, the number of cities that a country have, and the number of addresses of a country
Using the next query i get the country and the cities number
SELECT CO.country,COUNT(CI.city_id)
FROM city CI
INNER JOIN country CO ON CO.country_id = CI.country_id
GROUP BY CO.country;
Using this other one i get the addresses number
SELECT CO.country,COUNT(A.address_id)
FROM city CI
INNER JOIN address A ON A.city_id=CI.city_id
INNER JOIN country CO ON CI.country_id=CO.country_id
GROUP BY CO.country;
I was hinted to use Subqueries to get the desired results, but i can't find how to get all that in one table. Any suggestions?
This is actually a tricky problem. Your join approach can be made to work, with some slight modifications. The total count across each country group will give the number of addresses in that country. But to get the city count for a country, we can count the distinct city names in each country group. The need for DISTINCT here is that the join to the address table will cause each city name to replicated however many times an address appears in a given city. Taking the distinct city count gets around this problem.
SELECT
co.country_id,
COUNT(DISTINCT ci.city_id) AS city_cnt,
COUNT(a.city_id) AS address_cnt
FROM country co
INNER JOIN city ci
ON co.country_id = ci.country_id
INNER JOIN address a
ON ci.city_id = a.city_id
GROUP BY
co.country_id;
You can achieve the result using below sub query. This is basically to show how you can write it. Its recommended to use join(Refer answer from Tim Biegeleisen) than Sub queries as it gives good performance.
select
Co.Country,
(Select COUNT(1) from City Ci where Ci.countryid=co.countryid) CityCount,
(Select COUNT(1) from Address A Join city c on a.city_id=c.city_id where C.countryid=co.countryid) AddressCount
From Country Co

MySQL query with multiple count statements in a effective way

Let's have 4 simple tables:
TABLE region (id, name)
TABLE country (id, name, region_id)
TABLE organization (id, name, country_id)
TABLE person (id, name, organization_id)
I want to make a query, where I would have:
Region name
Person count for region
Country name
Person count for country
Organization name
Person count for organization
Person name
For instance:
Europe, 20, Slovakia, 2, University of Life, 1, Somebody
How can I make those counts in a effective way in MySQL?
I have a huge query, where I have inline selects for each count, but this is not at all efficient. I was considering sum with case, but I don't quite grasp the concept if I have thousands of organizations with different names...
SELECT r.name as region, count(DISTINCT r.id) as region_total,
c.name as city, count(DISTINCT c.id) as country_total,
o.name as organization, count(DISTINCT o.id) as organization_total,
COUNT(p.id) as person_total
FROM region r
JOIN country c
on r.id= c.region_id
JOIN organization o
ON c.id = o.country_id
JOIN person p
ON o.id p.organization_id
GROUP BY r.id, c.id, o.id
ORDER BY r.id, c.id, o.id
I dont see how you want to include a person name here

FInd the names of customers are interested in every artist

I am looking for customer names of customers who have an interest in all artists.
I understand that in relational algebra, I can use the division operator, however I do not understand the SQL format in doing so.
I have these tables with columns:
customer (customerID, firstname, lastname)
artist (artistID)
customer_interest_in_artists (artistID, customerID)
How would I go about doing this?
You could do this using a simple MIN() construct:
SELECT c.firstname, c.lastname, MIN(ci.customerID IS NOT NULL) AS interest_all
FROM artist
LEFT JOIN customer_interest_in_artists ci USING (artistID)
LEFT JOIN customer c USING (customerID)
GROUP BY c.customerID
HAVING interest_all = 1
You could either:
Identify the customers for whom the number of rows in the customer_interest_in_artists table is equal to the number of rows in the artists table.
or
Identify the customers for whom there does not exist a row in the customer_interest_in_artists table for one or more artists.
The first option os probably easiest to implement, as you can already probably get the number of rows per customer (hint: join, count(*), and group) and compare the number per customer with the number of rows in artists -- hint: having count(*) = (a subquery)
for ORACLE, I use this...
but i don't think mine is the most elegant of all answers, anyway, here it is!
SELECT c.FIRSTNAME, c.LASTNAME, c.CUSTOMERID
FROM DTOOHEY.CUSTOMER c, DTOOHEY.CUSTOMER_ARTIST_INT cai
WHERE c.CUSTOMERID = cai.CUSTOMERID
AND c.CUSTOMERID IN
(SELECT cai.CUSTOMERID
FROM DTOOHEY.CUSTOMER_ARTIST_INT cai
GROUP BY cai.CUSTOMERID
HAVING COUNT (*) = (SELECT COUNT (*) FROM DTOOHEY.ARTIST)
)
GROUP BY c.FIRSTNAME, c.LASTNAME, c.CUSTOMERID;
based on my limited knowledge, the flow of command is:
1) I am trying to get the customer ID, first name and last name of customer
2) I am getting it from the 2 tables (cai and c)
3) trying to join the 2 tables to give me a single data set
4) where the c.customerid is to be gathered in...
this is where the magic begins!!!
5) select the customerID (the single CustomerID)
6) from this table cai
7) group the result based on customerID, this is what gives the single CustomerID Value that you need...
8) having COUNT (*) - having the count of customerID value, to that of equal of the number of count of artists in the dtoohey.artist table.
the main logic is that the number of artist in the artist table (which is 11), exist in the CUSTOMER_ARTIST_INT in the same quantity. As such, we can tally the result of count from the ARTIST Table into the CUSTOMER_ARTIST_INT table.
I think these will useful to you
SELECT a.customerID, c.artistID from customer a
join customer_interest_in_artists b on a.customerID = b. customerID
join artist c on c.artistID = b.artistID
Thank you.

MySQL Join Query

I need to query the database by joining two tables. Here is what I have:
Table Town:
id
name
region
Table Supplier:
id
name
town_id
I currently have the following query which outputs all the Towns that belong to a given region:
SELECT id, name FROM Town WHERE region = 'North West';
Now I need to extend this query and create two further queries as follows:
Output the number of Suppliers for each Town
Output only the Towns that have 1 or more Supplier
I am using PHP for my scripts if that helps. I know I may be able to to get this data using PHP but in terms of performance it will probably be better if it is done in MySQL.
EDIT (27/07/10):
I now needs to extend this one last time - there is another table called Supplier_vehicles:
id
supplier_id
vehicle_id
A Supplier can have many Supplier_vehicles. The count (NumSupplier in this case) needs to now contain the total number of suppliers in a given town that have any of the given vehicle_id (IN condition):
SELECT * FROM Supplier s, Supplier_vehicles v WHERE s.id = v.supplier_id AND v.vehicle_id IN (1, 4, 6)
Need to integrate the above query into the existing JOIN query.
Count the number of suppliers.
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
LEFT OUTER JOIN Suppliers s ON s.town_id = t.id
GROUP BY t.id, t.name
Only towns that have at least one supplier
SELECT DISTINCT t.id, t.name
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
And you are 100% correct, the best place for this is an SQL query.
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
LEFT JOIN Suppliers s
[WHERE NumSupplier > 1]
GROUP BY t.id