Left join issue using having clause with sum - mysql

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

Related

SQL: select all customers that have all items expired

Consider the following DB structure
customer (id)
invoice (id, customer_id)
invoice_item (id, invoice_id, warranty_expiry)
I need to select all customers, where all their items are expired. Here is what I have so far
select * from customer c
inner join invoice i on c.id = i.customer_id
inner join invoice_item ii on i.id = ii.invoice_id
where ii.warranty_expiry < NOW()
group by c.id
having COUNT(ii.id) // <---
It feels like I should put something in HAVING clause, but I don't have an exact count of items for each client.
You can indeed use a having clause to ensure that the given customer has all their items expired. This works by moving the check on warranty_expiry from the where clause to the having clause, as follows:
select c.id
from customer c
inner join invoice i on c.id = i.customer_id
inner join invoice_item ii on i.id = ii.invoice_id
group by c.id
having max(ii.warranty_expiry >= NOW()) = 0
Note that select * and group by do not go along well (although older versions of MySQL do allow it by default). You should enumerate the columns that you want to retain in the select clause and in the group by clause.
You can simplify the query, because you don't need the customer table. Then I would go for:
select i.customer_id
from invoice i join
invoice_item ii
on i.id = ii.invoice_id
group by i.customer_id
having max(ii.waranty_expiry) < now();
This assumes that warnty_expiry is not null. If that is possible, then:
having max(ii.waranty_expiry) < now() and sum(ii.waranty_expiry is null) = 0;

Get total sum and count of a column in MySql

Is a nested SELECT statement possible in sql? I'm working on a problem and I can't seem to get the data that I want. This is the sql that Im querying:
SELECT derived.municipality, count(*) as counts, derived.bearing
from (SELECT m.name as municipality, count(*) as totalcount, sum(f.no_of_bearing_trees) as bearing
from farmer_profile f
inner join barangay b on f.barangay_id = b.id
inner join municipality m on b.municipality_id = m.id
inner join province p on m.province_id = p.id
group by b.name) as derived
group by derived.municipality, derived.bearing
Here is the sample data im working with. I want to get the sum of all the bearing and total counts when i put a where clause at the bottom (eg. where derived.bearing < 20). All of those bearings with less than 20 will totaled as well as their counts. I'm not sure if a subquery is needed again or not.
I suspect that you want to filter on municipalities whose bearing sum is less than 20. If so, you can use a having clause for this:
select
m.name as municipality,
count(*) as totalcount,
sum(f.no_of_bearing_trees) as bearing
from farmer_profile f
inner join barangay b on f.barangay_id = b.id
inner join municipality m on b.municipality_id = m.id
inner join province p on m.province_id = p.id
group by b.name
having sum(f.no_of_bearing_trees) < 20
MySQL is lax about column aliases in the having clause, so you can also do:
having bearing < 20

MySQL Help Cleaning Total Counts

Having an issue with a JOIN statement.
I'm trying to get a total per name, and not the current 1 with a ton of other same name records
SELECT a.`name`,
(SELECT COUNT(b.`id`)
FROM `host1_hosting` AS b
WHERE b.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`) AS HostingCount
FROM `host1_servers` AS a
LEFT JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
ORDER BY a.`name`
Example Returned
Name HostingCount
Name 1
Name 1
Name 1
Where it should be:
Name 3
I'm sure this is simple, but it's early monday, and I'm foggy
Query 2
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` AS a
JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
JOIN `host1_hosting` AS d ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
ORDER BY a.`name`
Gets me 1 name record, but a total of all COUNT
Your second query needs a group by:
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` AS a
JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
JOIN `host1_hosting` AS d ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
GROUP BY a.name
ORDER BY a.`name`;
Without the GROUP BY, MySQL interprets the query as an aggregation query to produce one row. The count() is the overall count. The column name is chosen arbitrarily from one of the rows (using a MySQL extension that wouldn't work in any other database).
EDIT:
If you want to keep all names from the first table and do the count, use left outer join:
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` a LEFT OUTER JOIN
`host1_servers_host1_hosting_1_c` c
ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id` LEFT OUTER JOIN
`host1_hosting` d
ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
GROUP BY a.name
ORDER BY a.`name`;

MySQL using COUNT, LEFT JOIN and GROUP by returning undesired results

I need some help to get the desired results, which would in this case, be 7 (the number of rows in the products table that would match).
What I am instead getting is 7 rows with a count based on the the number of rows returned in the LEFT JOIN.
SELECT count(p.id) as theCount
FROM products p
left join tablea a on p.id = a.productId
left join tableb b on p.id = b.productId
WHERE (a.col = 'val' or b.col = 'val')
group by p.id
If I do not group by p.id, I get back 28 rows, which is all of the rows from the LEFT JOIN.
I know it's something simple, but I can't figure it out.
Thanks.
select count(distinct p.id), perhaps? Since you're pulling from two different tables, you're going to get a mismash of (p.id, a.col, b.col) being (xxx, null, yyy) and (xxx, yyy, null)
You shouldn't join the one-to-many relationships if all you want is the count of products.
Put your filter condition in the WHERE clause.
SELECT count(*) as theCount
FROM products p
WHERE p.id IN (
SELECT a.productId
FROM tablea a
WHERE a.productId = p.id AND a.col = 'val'
UNION
SELECT b.productId
FROM tableb b
WHERE b.productId = p.id AND b.col = 'val'
)

MySQL Join Query (possible two inner joins)

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