MySQL right join two tables - mysql

I have three tables: hospitals, inspections, and issues. A hospital can have zero or more inspections. Inspections can have zero or more issues. I need to get a table that has all violations with the hospital that they were observed in as well as the date. Tables look like:
Business Table b
----------------------------
|id|name|address|city|state|
Inspection Table i
---------------------
|id|business_id|date|
Issue Table v
-----------------------------------
|id|business_id|inspection_id|desc|
What I need, ordered by i.date desc is:
Query result
--------------------------------
|b.name|b.address|i.date|v.desc|
There will be more than one issue per inspection so I need a row for each as above. That's what I am getting, but the latest inspection data is returned for every issue even though they were observed on different dates.
Here is what I have for my query:
SELECT b.business_name AS Name, b.address, b.city, b.state, i.date, v.desc
FROM business_table AS b
RIGHT JOIN inspection_table i ON i.business_id = b.id
RIGHT JOIN issue_table v ON v.inspection_id = i.id
ORDER BY i.date DESC

Try this
SELECT b.business_name AS Name, b.address, b.city, b.state, i.date, v.desc
FROM issue_table v
LEFT JOIN inspection_table i ON i.id = v.inspection_id
LEFT JOIN business_table AS b ON v.business_id = b.id
ORDER BY v.business_id, i.date DESC

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

Left join latest inspection date to establishments using MySQL

I have a table of establishments and I want to return a result set with the latest inspection date from the inspections table. Right now I have:
SELECT business_table.business_name, business_table.address, inspection_table.date
FROM business_table
LEFT JOIN inspection_table ON business_table.id = inspection_table.business_id
WHERE inspection_table.date = (
SELECT MAX(date)
FROM inspection_table)
The problem is I get only one result with the latest inspection date. I need all of the establishments returned. The query will need to be efficient because I have about 600K establishments and 3Million inspections.
You are using an outer join. When a business record has no matching inspection record, then an empty inspection record is created an joined instead. So that outer-joined inspection record will have all columns NULL. Then you have WHERE inspection_table.date = (...). This dismisses all outer-joined records again, because NULL will never match.
Use AND instead, to make the condition part of the WHERE clause:
SELECT
b.business_name,
b.address,
i.date
FROM business_table b
LEFT JOIN inspection_table i
ON i.business_id = b.id
AND i.date = (SELECT MAX(date) FROM inspection_table);
Ah, you are not looking for the latest inspection date at all. You are looking for the latest inspection date per business.
In your solution you are still using an outer join that doesn't work. Don't do that. Either use an outer join and use it properly or use an inner join if you are fine with that.
Here is how to get the latest inspection per business with an outer join (so you also show businesses that have had no inspection, yet):
SELECT
b.business_name,
b.address,
i.date,
...
FROM business_table b
LEFT JOIN inspection_table i
ON i.business_id = b.id
AND (i.business_id, i.date) IN
(
SELECT business_id, MAX(date)
FROM inspection_table
GROUP BY business_id
);
Same with joins:
SELECT
b.business_name,
b.address,
i.date,
...
FROM business_table b
LEFT JOIN
(
SELECT business_id, MAX(date) AS date
FROM inspection_table
GROUP BY business_id
) latest ON latest.business_id = b.id
LEFT JOIN inspection_table i
ON i.business_id = latest.business_id
AND i.date = latest.date;
I ended using the following:
SELECT business_table.business_name AS Name, business_table.address AS Address, business_table.city AS City, business_table.state AS Province, inspection_table.rating AS Rating, inspection_table.date AS "Inspected"
FROM business_table
LEFT JOIN inspection_table ON business_table.id = inspection_table.business_id
WHERE inspection_table.date = (
SELECT MAX(inspection_table.date)
FROM inspection_table
WHERE business_table.id = inspection_table.business_id)
ORDER BY inspection_table.date DESC

emulated of full join return wrong values

I have two tables that doesn't have relationship and I do a left and right join to simulate a full join on them and select some data.
the manner of displaying data is right but values are wrong, its looks like they selected more than once.
my tables are something like this :
TABLE 1 (bargains)
trade_date ---- profit
TABLE 2 (general_cost)
date ----- cost
this is the query that i write :
select b.trade_date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0)
from bargains as b
left join general_cost as g on b.trade_date = g.date group by b.trade_date
union
select g.date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0) from
bargains as b
right join general_cost as g on b.trade_date = g.date group by g.date
this is the result of query :
for example in date 1395-9-28 the sum of profit column should be 440 and sum of cost column should be 800
if it's help you should know that there is three row with this date in bargains table and two row in general_cost table
Yes, your query duplicates the matching records because those are included in both left and the right join. You need to exclude the matching records from one of the queries. I usually exclude them from the 2nd query of the union:
select b.trade_date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0)
from bargains as b
left join general_cost as g on b.trade_date = g.date group by b.trade_date
union
select g.date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0) from
bargains as b
right join general_cost as g on b.trade_date = g.date
where b.date is null //include only the records from general_cost that are not matched
group by g.date
UPDATE
If you have multiple records in both tables with the same date, then you need to do the summing per table separately in subqueries, otherwise the matching records do get duplicated:
select b.trade_date, b.profit, coalesce(g.cost,0)
from (select trade_date, sum(profit) as profit from bargains group by trade_date) as b
left join (select date, sum(cost) as cost from general_cost group by date) as g on b.trade_date = g.date
union
select g.date, 0, sum(g.cost) from //all profits has been summed up in the above query, so here we can use 0 in place of profit
bargains as b
right join general_cost as g on b.trade_date = g.date
where b.trade_date is null //include only the records from general_cost that are not matched
group by g.date

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 query, dealing with active and inactive products

Facing a problem and not getting the hint for a few hours. Maybe onyone can help me out.
Have the following query which shows the Topsellers. So the status of the product (active or not) is saved in b.Article_Status (0=inactive, 1=active).
How do I get the products of the result list which have no active product in the productfamily at the moment. But the product shall still be shown if an old one was ordered (and so is in table order_items) is now inactive and the active one was not ordered yet.
Actual query looks as follow. Already fund a solution which works when the actual active product has been ordered once, but still the problem with the mentioned case.
SELECT count( a.order_itemid ) AS numOrders, c.Product_ID, c.Product_Name, d.producer_name
FROM order_items a
LEFT OUTER JOIN product_article b ON b.Article_ID = a.order_itemid
LEFT OUTER JOIN product c ON b.Article_Productid = c.Product_ID
LEFT OUTER JOIN producer d ON c.Product_Producer = d.producer_id
GROUP BY c.Product_ID
ORDER BY `numOrders` DESC
Solution was a WHERE EXISTS subquery
SELECT count( a.order_itemid ) AS numOrders, c.Product_ID, c.Product_Name, d.producer_name
FROM order_items a
LEFT OUTER JOIN product_article b ON b.Article_ID = a.order_itemid
LEFT OUTER JOIN product c ON b.Article_Productid = c.Product_ID
LEFT OUTER JOIN producer d ON c.Product_Producer = d.producer_id
WHERE EXISTS (SELECT * FROM product_article x WHERE c.Product_ID = x.Article_Productid AND x.Article_Status = 1)
GROUP BY c.Product_ID
ORDER BY `numOrders` DESC
LIMIT 5