MySQL Join Query (possible two inner joins) - mysql

I currently have the following:
Table Town:
id
name
region
Table Supplier:
id
name
town_id
The below query returns the number of suppliers for each town:
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
GROUP BY t.id, t.name
I now wish to introduce another table in to the query, Supplier_vehicles. A supplier can have many vehicles:
Table Supplier_vehicles:
id
supplier_id
vehicle_id
Now, the NumSupplier field needs to return the number of suppliers for each town that have any of the given vehicle_id (IN condition):
The following query will simply bring back the suppliers that have any of the given vehicle_id:
SELECT * FROM Supplier s, Supplier_vehicles v WHERE s.id = v.supplier_id AND v.vehicle_id IN (1, 4, 6)
I need to integrate this in to the first query so that it returns the number of suppliers that have any of the given vehicle_id.

SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
WHERE s.id IN (SELECT sv.supplier_id
FROM supplier_vehicles sv
WHERE sv.vehicle_id IN (1,4,6))
GROUP BY t.id, t.name
Or you could do an INNER JOIN (as your supplier join is INNER, but this will remove towns with no suppliers with those vehicles) and change the COUNT(s.id) TO COUNT(DISTINCT s.id)

If I remember correctly, you can put your second query inside the LEFT OUTER JOIN condition.
So for example, you can do something like
...
LEFT OUTER JOIN (SELECT * FROM Suppler s, Supplier_vehicles ......) s ON s.town_id=t.id
In that way you are "integrating" or combining the two queries into one. Let me know if this works.

SELECT t.name, count(s.id) as NumSupplier
FROM Town t
LEFT OUTER JOIN Suppliers s ON t.id = s.town_id
LEFT OUTER JOIN Supplier_vehicles v ON s.id = v.supplier_id
WHERE v.vehicle_id IN (1,4,6)
GROUP BY t.name

Related

Left join issue using having clause with sum

I have two tables, products(id, name) and products_cost(id, sid, cost).
I'm trying to get a list of products whose total cost is less than 1000 including products that has no cost.
I've tried this:
SELECT a.name, SUM(b.cost) AS price
FROM products a
LEFT JOIN products_cost b
ON a.id = b.sid
GROUP BY a.name
HAVING SUM(b.cost)<1000;
The above SQL only gives the list of product name that includes cost and I want the output list to include product name that doesn't include cost as well.
I think you need to check for null values in the having clause:
SELECT p.name, SUM(pc.cost) AS price
FROM products p LEFT JOIN
products_cost pc
ON p.id = b.sid
GROUP BY p.name
HAVING SUM(pc.cost) < 1000 OR SUM(pc.cost) IS NULL;
Note that I also fixed the logic (salary doesn't makes sense in the SUM()). And I introduced meaningful table aliases -- abbreviations for the table names.
You can also add an IFNULL expression in your HAVING clause
SELECT a.name, SUM(b.cost) AS price
FROM products a
LEFT JOIN products_cost b
ON a.id = b.sid
GROUP BY a.name
HAVING SUM(IFNULL,b.cost,0)<1000;
use subquery and join
select p.*,a.s as cost from product p left join (
select pid,sum(cost) as s
from products_cost
group by pid
having s<1000 ) a on p.id=a.pid

SQL Retrieving repeated rentals

Question:
write a SQL query that retrieves repeated rentals in which the same
customer rents the same movie more than once. if a customer rents the > same movie multiple times, the output should show this (customer's name, movie's title) combination only once
There are 3 tables:
customer table: (id, name, country, created_date)
movie table: (id, title, duration, release_year)
rental table: (id, customer_id, movie_id)
i currently have:
select customer.name, movie.title
from ((rental inner join customer on rental.customer_id = customer.id) inner join movie on rental.movie_id = movie.id)
group by customer.name, movie.title
having count(*) > 1;
but it is incorrect - any thoughts?
Rewrite your query as:
SELECT c.name, m.title
FROM rental AS r
INNER JOIN customer AS c ON r.customer_id = c.id
INNER JOIN movie AS m ON r.movie_id = m.id
GROUP BY c.name, m.title
HAVING count(*) > 1
;
Your JOIN statements has wrong syntax
EDIT: to avoid naming duplicates change GROUP BY statements:
GROUP BY c.id, m.id

SQL query find supplier with lowest price for each part

I need to find the supplier with the lowest price for each part.
Tables: suppliers(sid, sname, address), parts(pid, pname, colour), catalog(sid, pid, cost)
This works:
SELECT
sname, pid
FROM
(SELECT
*
FROM
suppliers
NATURAL JOIN catalog
NATURAL JOIN (SELECT
pid, MIN(cost) AS min_cost
FROM
catalog
GROUP BY (pid)) AS m
HAVING cost = min_cost) AS n
But when I try to shorten it to the following I get an error that there is an unknown cost in the having clause:
SELECT
sname, pid
FROM
suppliers
NATURAL JOIN
catalog
NATURAL JOIN
(SELECT
pid, MIN(cost) AS min_cost
FROM
catalog
GROUP BY (pid)) AS m
HAVING cost = min_cost
Why can't it find the cost? Isn't the cost in the table because I've joined the subquery to catalog?
EDIT
I changed it to use INNER JOIN instead of NATURAL JOIN as per suggestions, but I'm still getting the same error. New query:
SELECT
s.sname, m.pid
FROM
suppliers s
INNER JOIN
catalog c ON s.sid = c.sid
INNER JOIN
(SELECT
pid, MIN(cost) AS min_cost
FROM
catalog
GROUP BY (pid)) AS m ON c.pid = m.pid
HAVING cost = min_cost
EDIT_2
The problem was not the JOIN but the HAVING, which should actually be WHERE, as shown by bbrumm's answer.
I would suggest a query like this:
SELECT
supplier.sname,
catalog.pid
FROM suppliers
INNER JOIN catalog ON suppliers.supplier_id = catalog.supplier_id
INNER JOIN
(SELECT
pid, MIN(cost) AS min_cost
FROM catalog
GROUP BY (pid)) AS m
ON catalog.pid = m.pid
WHERE catalog.cost = m.min_cost;
I've made a few assumptions on your column names (e.g. supplier_id) that you may need to change. A point could be made that the "cost=min_cost" is part of the JOIN so it could go there as well. I've also not included table aliases as while it's best practice, it's not required.

MySQL Ordering by SubQuery

I'm trying to pull a list of customers who place the most orders. I can't seem to figure out how to keep the list ordered by the sub query. Here's my query:
SELECT
c.*,
state.abbreviation AS state,
country.abbreviation AS country
FROM main_customers AS c
LEFT JOIN dict_stateProvince AS state ON c.state = state.id
LEFT JOIN dict_country AS country ON c.country = country.id
WHERE c.id IN (SELECT customerId
FROM main_orders
GROUP BY customerId
ORDER BY COUNT(*) DESC)
LIMIT 50;
How do I keep the order of the main query the same as the subquery?
Your subquery doesn't have a real order, because IN ignores the ordering. But the intention is clear. So, use a join:
SELECT c.*,
state.abbreviation AS state,
country.abbreviation AS country
FROM main_customers AS c JOIN
(SELECT customerId, COUNT(*) as cnt
FROM main_orders
GROUP BY customerId
) mo
ON mo.customerId = c.id LEFT JOIN
dict_stateProvince AS state
ON c.state = state.id LEFT JOIN
dict_country AS country
ON c.country = country.id
ORDER BY mo.cnt DESC;

Mysql query select non-members

I have 2 tables:
Customers:
- ID
- NAME
Modulemembers:
- ID
- customer_id
- enabled
I use this to enable a function of my site for some customers.
I have a form where the admin of the site can add the module for a customer, so i need a query that looks for customers that are NOT member of the modulemembers table.
I made this:
SELECT customers.id,
customers.name
FROM customers,
modulemembers
WHERE customers_id != modulemembers.cust_id
ORDER BY customers.name
but it does not work. What am I doing wrong?
You can use NOT EXISTS in the WHERE clause to get the result:
SELECT c.id,
c.name
FROM customers c
WHERE not exists (select customer_id
from modulemembers m
where c.id = m.customer_id)
order by c.name
You can use a LEFT OUTER JOIN and filter based on NULLs. The query below will pull in all results from the customers table and the modulemembers table. If there is not a match in the modulemembers table then the custid will be NULL.
SELECT c.id, c.name
FROM customers c
LEFT OUTER JOIN modulemembers m ON c.customers_id = m.cust_id
WHERE m.custid IS NULL
ORDER BY c.name
Try something like this:
SELECT c.ID AS customerId,
c.NAME AS fullName,
m.ID AS memberId
FROM Customers AS c
LEFT JOIN Modulemembers AS m ON m.customer_id = c.ID
WHERE m.ID IS NULL;