Get rows that doesn't match on another table - mysql

I have this working query that generate data the way i like it to happened but now i want it to add another clause statement that will get rows from tbl_loan_master that was not on the tbl_loanledger.
Example Data:
tbl_borrowers
------------------------------------------
| id | first_name | last_name | deleted |
| 1 | Joe | Smith | 0 |
| 2 | Lily | Mag | 0 |
| 3 | Zen | Green | 0 |
| 4 | Kim | Chan | 0 |
| 5 | Bob | Mac | 1 |
| 6 | Ben | Cork | 0 |
------------------------------------------
tbl_loan_master
----------------------------------------------------------------------
| id | borrowers_id | loan | date_created | due_date | deleted |
| 1 | 4 | 300 | 2016/04/28 | 2017/04/28 | 0 |
| 2 | 1 | 100 | 2016/05/05 | 2017/05/05 | 0 |
| 3 | 2 | 500 | 2016/06/08 | 2017/06/08 | 0 |
| 4 | 1 | 200 | 2016/06/13 | 2017/06/13 | 0 |
| 5 | 3 | 150 | 2016/06/15 | 2017/06/15 | 0 |
| 6 | 6 | 50 | 2016/06/16 | 2017/06/16 | 0 |
----------------------------------------------------------------------
tbl_loanledger
------------------------------------------------------------------------------
| id | borrowers_id | loanmaster_id | payment | balance| date_created | deleted
| 1 | 4 | 1 | 50 | 250 | 2016/05/28 | 0
| 2 | 1 | 2 | 20 | 80 | 2016/05/25 | 0
| 3 | 1 | 2 | 30 | 50 | 2016/06/01 | 0
| 4 | 2 | 3 | 100 | 400 | 2016/06/09 | 0
| 5 | 2 | 3 | 50 | 350 | 2016/06/10 | 0
| 6 | 3 | 4 | 50 | 150 | 2016/06/16 | 0
------------------------------------------------------------------------------
Here is the working query:
$query = "SELECT `tbl_borrowers`.* , `tbl_loanledger`.*, `tbl_loan_master`.*
FROM `tbl_borrowers`
LEFT JOIN `tbl_loanledger`
ON `tbl_borrowers`.id = `tbl_loanledger`.borrower_id
LEFT JOIN `tbl_loan_master`
ON `tbl_loan_master`.id = `tbl_loanledger`.loanmaster_id
WHERE `tbl_borrowers`.deleted = 0 AND `tbl_loanledger`.deleted = 0 AND MONTH ( `tbl_loanledger`.date_created) = MONTH(CURRENT_DATE)
GROUP BY `tbl_loanledger`.borrower_id
ORDER BY `tbl_borrowers`.last_name";
The expected result will output the borrowers with their last transaction on the current month (which is June) in loan ledger and also in loans accounts. Like Ben Cork which is not on the loan ledger, he is on the loan accounts but i want to output him on the result set. The deleted column means if it is 0 it indicates it is active if it is 0 it means it has been deleted.
Expected Result:
|First Name | Last Name | Due Date | Balance |
| Ben | Cork | 2017/06/16 | 50 |
| Joe | Smith | 2017/06/13 | 50 |
| Lily | Mag | 2017/06/08 | 350 |
| Zen | Green | 2017/06/15 | 150 |

Try following;)
select
tb.first_name, tb.last_name, coalesce(tlm.Loan, 0) as Loan, coalesce(t.`Amount Paid`, 0) as `Last Amount Paid`
from tbl_borrowers tb
left join tbl_loan_master tlm
on tb.id = tlm.borrowers_id
left join (
select t1.*
from tbl_loanledger t1
inner join (
select max(id) as id
from tbl_loanledger
group by borrowers_id, loanmaster_id
) t2 on t1.id = t2.id
) t
on tb.id = t.borrowers_id
and tlm.id = t.loanmaster_id
SQLFiddle DEMO HERE

Related

MySQL select users who was in range of dates

Hello :) I need to create sql which will calculate advances for user but only for that one who was in work. Tables looks like:
users:
| id | firstName | lastName |
| -- | --------- | -------- |
| 1 | John 1 | Test 1 |
| 2 | John 2 | Test 2 |
| 3 | John 3 | Test 3 |
| 4 | John 4 | Test 4 |
users_advances:
| id | user_id | amount | d_add | status_id |
| -- | ------- | ------ | ---------- | --------- |
| 1 | 1 | 100 | 2022-07-09 | 1 |
| 2 | 1 | 50 | 2022-07-10 | 2 |
| 3 | 2 | 100 | 2022-07-03 | 1 |
| 4 | 2 | 50 | 2022-07-05 | 2 |
| 5 | 2 | 100 | 2022-03-09 | 1 |
| 6 | 1 | 50 | 2022-07-02 | 2 |
users_arrivals
| id | user_id | start_date | end_date |
| -- | ------- | ---------- | ---------- |
| 1 | 1 | 2022-09-01 | 2022-09-30 |
| 2 | 2 | 2022-09-22 | 2022-09-25 |
| 3 | 3 | 2022-09-19 | 2022-09-25 |
I created SQL
SELECT u.id AS user_id, CONCAT(u.firstName, SPACE(1), u.lastName) AS fullName, IFNULL(SUM(uz.amount), 0) AS suma
FROM users u
LEFT JOIN users_advances uz ON uz.user_id = u.id AND (uz.d_add BETWEEN '2022-09-19' AND '2022-09-25') AND ((uz.status_id = 1) OR (uz.status_id = 2))
LEFT JOIN users_arrivals po ON po.user_id = u.id
WHERE po.start_date <= '2022-09-19' AND po.end_date >= '2022-09-24'
GROUP BY u.id
but it doesnt return me user 2 who had start_date at 2022-09-22.

How do I count the roles of group members in Moodle 3.5 courses in pure SQL?

I'm trying to write a MySQL report on the Moodle 3.5 database to return a list of courses, some enrolment details, course's groups (if any), and count the members in these groups if they are enrolled as certain roles. My query displays the category, course, and groupname columns correctly (null is intentional here), but the groupstudentcount and groupspnetcount columns are incorrect in some rows. Can you see where I've gone wrong?
Intended Results:
| category | course | group | groupmembertotal | groupstudentcount | groupspnetcount |
|----------------------------|----------------------|-----------------------------|------------------|-------------------|-----------------|
| Commercial Cookery | Baking | NULL | 0 | 0 | 0 |
| Commercial Cookery | Cookies | Newcastle | 0 | 0 | 0 |
| Commercial Cookery | Cookies | Ultimo | 0 | 0 | 0 |
| Miscellaneous | Dummy Course | NULL | 0 | 0 | 0 |
| Miscellaneous | TDC Unit No Students | Course template_2018 course | 0 | 0 | 0 |
| Miscellaneous | TDC Unit No Students | Group 1 | 0 | 0 | 0 |
| Miscellaneous Sub-category | Apples | Newcastle | 1 | 0 | 1 |
| Miscellaneous Sub-category | Apples | Ultimo | 2 | 2 | 0 |
| Miscellaneous Sub-category | Test course | Newcastle | 2 | 1 | 1 |
| Miscellaneous Sub-category | Test course | Ultimo | 1 | 1 | 0 |
| TDC | TDC Course Template | NULL | 0 | 0 | 0 |
| TDC | TDC Unit Tempate | Course template_2018 course | 1 | 0 | 1 |
| TDC | TDC Unit Tempate | Group 1 | 1 | 1 | 0 |
Actual Results:
| category | course | group | groupmembertotal | groupstudentcount | groupspnetcount |
|----------------------------|----------------------|-----------------------------|------------------|-------------------|-----------------|
| Commercial Cookery | Baking | NULL | 0 | 0 | 0 |
| Commercial Cookery | Cookies | Newcastle | 0 | 0 | 0 |
| Commercial Cookery | Cookies | Ultimo | 0 | 0 | 0 |
| Miscellaneous | Dummy Course | NULL | 0 | 0 | 0 |
| Miscellaneous | TDC Unit No Students | Course template_2018 course | 0 | 0 | 0 |
| Miscellaneous | TDC Unit No Students | Group 1 | 0 | 0 | 0 |
| Miscellaneous Sub-category | Apples | Newcastle | 1 | 0 | 1 |
| Miscellaneous Sub-category | Apples | Ultimo | 2 | 2 | 0 |
| Miscellaneous Sub-category | Test course | Newcastle | 2 | 0 (should = 1) | 2 (should = 1) |
| Miscellaneous Sub-category | Test course | Ultimo | 1 | 1 | 0 |
| TDC | TDC Course Template | NULL | 0 | 0 | 0 |
| TDC | TDC Unit Tempate | Course template_2018 course | 1 | 0 | 1 |
| TDC | TDC Unit Tempate | Group 1 | 1 | 1 | 0 |
Data tables:
mdl_course_categories cat
| id | name |
|----|----------------------------|
| 1 | Miscellaneous |
| 2 | TDC |
| 3 | Miscellaneous Sub-category |
| 4 | Test |
| 5 | Course Offerings |
| 6 | Commerical Cookery |
mdl_course c
| id | category | fullname |
|----|----------|-----------------------|
| 1 | 0 | Localhost Moodle v3.5 |
| 2 | 2 | TDC Unit Tempate |
| 3 | 2 | TDC Course Template |
| 4 | 1 | Dummy Course |
| 5 | 1 | TDC Unit No Students |
| 6 | 6 | Baking |
| 7 | 6 | Cookies |
| 8 | 3 | Apples |
| 9 | 3 | Test course |
mdl_groups g
| id | courseid | name |
|----|----------|-----------------------------|
| 1 | 2 | Group 1 |
| 2 | 2 | Course template_2018 course |
| 3 | 5 | Group 1 |
| 4 | 5 | Course template_2018 course |
| 5 | 7 | Newcastle |
| 6 | 7 | Ultimo |
| 7 | 9 | Newcastle |
| 8 | 9 | Ultimo |
| 9 | 8 | Newcastle |
| 10 | 8 | Ultimo |
mdl_groups_members gm
| id | groupid | userid |
|----|---------|--------|
| 1 | 2 | 2 |
| 3 | 1 | 4 |
| 4 | 7 | 3 |
| 5 | 8 | 4 |
| 6 | 7 | 2 |
| 7 | 9 | 2 |
| 8 | 10 | 4 |
| 9 | 10 | 3 |
mdl_context ct where contextlevel = 50 (50 is the course context, where these enrollments I'm reporting on occur as per the documentation)
| id | contextlevel | instanceid |
|-----|--------------|------------|
| 2 | 50 | 1 |
| 24 | 50 | 2 |
| 48 | 50 | 3 |
| 63 | 50 | 4 |
| 85 | 50 | 5 |
| 113 | 50 | 6 |
| 114 | 50 | 7 |
| 115 | 50 | 8 |
| 116 | 50 | 9 |
mdl_role_assignments ra
| id | roleid | contextid | userid |
|----|--------|-----------|--------|
| 1 | 5 | 24 | 3 |
| 2 | 5 | 48 | 3 |
| 3 | 5 | 24 | 4 |
| 4 | 5 | 48 | 4 |
| 5 | 5 | 24 | 2 |
| 6 | 9 | 116 | 2 |
| 7 | 5 | 116 | 3 |
| 8 | 5 | 116 | 4 |
| 11 | 5 | 115 | 4 |
| 12 | 9 | 115 | 2 |
| 13 | 9 | 113 | 2 |
| 15 | 5 | 115 | 3 |
Here's my query so far:
/* edit: re-wrote the query slightly differently than the original posted, and it removes some errors, but not all. I've updated the tables above with the required information. Old query here: https://pastebin.com/qBfh7kqV */
select
cat.name category,
c.fullname course,
g.name groupname,
count(distinct gm.userid) groupmembercount,
case when ra.roleid = 5
then count(distinct ra.userid)
else 0
end as groupstudentcount,
case when ra.roleid = 9
then count(distinct ra.userid)
else 0
end as groupspnetcount
from mdl_course_categories cat
join mdl_course c on cat.id = c.category
join mdl_context ct on c.id = ct.instanceid
and ct.contextlevel = 50
left join mdl_groups g on ct.instanceid = g.courseid
and ct.contextlevel = 50
left join mdl_groups_members gm on g.id = gm.groupid
left join mdl_role_assignments ra on gm.userid = ra.userid
group by
cat.id,
c.id,
g.id
order by
cat.name asc,
c.fullname asc,
g.name asc
I think the join on the context is wrong. The mdl_groups table has a courseid, so you should use that to join to mdl_course.
The mdl_role_assignments table has a contextid, so this is the one that needs the context join. So your join would look like
FROM mdl_course_categories cat
JOIN mdl_course c ON cat.id = c.category
JOIN mdl_context ctx ON c.id = ctx.instanceid AND ctx.contextlevel = 50
LEFT JOIN mdl_groups g ON g.courseid = c.id
LEFT JOIN mdl_groups_members gm ON g.id = gm.groupid
LEFT JOIN mdl_role_assignments ra ON gm.userid = ra.userid AND ra.contextid = ctx.id

How to combine results from 2 queries without common "GROUP BY" element

I am trying to count the number of instances of different types and group them by a field denoting location. However, the location from my second query is obtained by a sub string of yet another field and is 300 less than the desired number.
The goal is to sum phone types by location. For IP phones (type 9608, b189, and 9611), the location is determined by the nr (network region). For analog devices (type cordless, 2500, and fax) the location is determined by the first 3 characters on the port.
I have each individual query working, for the most part, as desired. I have 2 issues:
The nr from the second query needs to have 300 added to the result.
I have not been able to combine the 2 queries together to obtain the desired, single result.
Example Data Set
+------+--------------+---------+-----------------------------+------+------+-----------+------+------+--------+-----------+------+----------------+------+
| id | extension | port | name | cp1 | cor | type | cp2 | cos | tenent | prod_id | tcp | ip | nr |
+------+--------------+---------+-----------------------------+------+------+-----------+------+------+--------+-----------+------+----------------+------+
| 62 | 111-000-0201 | S00215 | Baker Mail Rm | | 2 | 9608 | | 1 | 4 | IP_Phone | tcp | 172.20.94.76 | 308 |
| 63 | 111-000-0202 | S00216 | Baker Copy Rm | | 2 | 9608 | | 1 | 4 | | | | |
| 66 | 111-000-0205 | S00235 | Baker Conference Rm 2 | | 2 | b189 | | 1 | 4 | IP_Phone | tcp | 172.20.94.22 | 308 |
| 123 | 111-000-0626 | 008V301 | Baker Cordless | | 2 | cordless | | 1 | 1 | | | | |
| 145 | 111-000-1200 | S12329 | JEF-MAIN Dental 1 | | 2 | 9611 | | 1 | 1 | IP_Phone | tcp | 172.20.195.160 | 490 |
| 885 | 999-888-3025 | 190V203 | JEF WP Admin Conf Rm | | 2 | 2500 | | 1 | 1 | | | | |
| 890 | 999-888-1561 | 190V201 | JEF-GATO Clinic Fax | | 2 | fax | | 1 | 1 | | | | |
| 993 | 111-777-0202 | S00256 | Test Rm | | 2 | 9608 | | 1 | 4 | IP_Phone | | 172.20.190.45 | 303 |
| 994 | 111-777-0212 | S00217 | Test Rm 2 | | 2 | 9608 | | 1 | 4 | IP_Phone | | 172.20.190.46 | 303 |
Query 1
SELECT nr,
sum(type='9608') '9608',
sum(type='9611') '9611',
sum(type='b189') 'b189',
count(*) 'Total IP'
FROM station GROUP BY nr;
Result 1
+------+------+------+------+----------+
| nr | 9608 | 9611 | b189 | Total IP |
+------+------+------+------+----------+
| 308 | 1 | 0 | 1 | 2 |
| 490 | 0 | 1 | 0 | 1 |
| 303 | 2 | 0 | 0 | 2 |
Query 2
SELECT SUBSTR(port, 1, 3) AS 'nr',
sum(type='fax') 'fax',
sum(type='2500') 'analog',
sum(type='cordless') 'cordless',
count(*) 'Total Analog'
FROM station
WHERE port LIKE '___V%'
GROUP BY SUBSTR(port, 1, 3);
Result 2
+------+------+--------+----------+--------------+
| nr | fax | analog | cordless | Total Analog |
+------+------+--------+----------+--------------+
| 008 | 0 | 0 | 1 | 1 |
| 190 | 1 | 1 | 0 | 2 |
The desired result is
+------+------+------+------+----------+------+--------+----------+--------------+
| nr | 9608 | 9611 | b189 | Total IP | fax | analog | cordless | Total Analog |
+------+------+------+------+----------+------+--------+----------+--------------+
| 308 | 1 | 0 | 1 | 2 | 0 | 0 | 1 | 1 |
| 490 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 2 |
| 303 | 2 | 0 | 0 | 2 | 0 | 0 | 0 | 0 |
Is this what you need? I used inner join. check my answer.
SELECT s1.nr,
sum(type='9608') '9608',
sum(type='9611') '9611',
sum(type='b189') 'b189',
count(*) 'Total IP' ,
fax,
analog,
cordless,
TotalAnalog
FROM station s1
INNER JOIN (
SELECT SUBSTR(port, 1, 3) AS 'nr',
sum(type='fax') 'fax',
sum(type='2500') 'analog',
sum(type='cordless') 'cordless',
count(*) 'TotalAnalog'
FROM station
WHERE port LIKE '___V%'
GROUP BY SUBSTR(port, 1, 3)
) as s2
ON s1.nr = s2.nr + 300
GROUP BY nr;
working Fiddle

select sum of product price for every 1 id productcategory

i want to find solution for my query problem. I need to find the SUM of all priceProduct*quantity and separated with each of productcategory. I have already made a query, but it takes longer time to executed it. this is my query,
SELECT
pb.ProductCategoryID,
pb.ProductCategoryDescription,
(SELECT
SUM((SELECT pd.HPP FROM `price details` pd WHERE pd.ProductID = pdt.ProductID ORDER BY pd.PriceDetailID DESC LIMIT 1)*
(SELECT StockProductBallance FROM `stock product` sp WHERE sp.ProductID = pdt.ProductID ORDER BY sp.StockProductID DESC LIMIT 1))
FROM product pdt
WHERE pdt.ProductCategoryID = pb.ProductCategoryID
) AS Total
FROM `product category` pb
GROUP BY pb.ProductCategoryID
this my example table
table product:
+------+-------+
| id_p | id_pc |
+------+-------+
| 1 | 3 |
| 2 | 4 |
| 3 | 3 |
| 4 | 4 |
+------+-------+
table productcategory:
+-------+---------+
| id_pc | pc_name |
+-------+---------+
| 3 | new_pc |
| 4 | old_pc |
+-------+---------+
table price details:
+---------------+------+-----+
| PriceDetailID | id_p | hpp |
+---------------+------+-----+
| 1 | 1 | 100 |
| 2 | 1 | 110 |
| 3 | 1 | 120 |
| 4 | 2 | 200 |
| 5 | 2 | 210 |
| 6 | 2 | 220 |
+---------------+------+-----+
table stockProduct:
+-----------------+------+---------------+
| id_stockProduct | id_p | stockballance |
+-----------------+------+---------------+
| 1 | 1 | 10 |
| 2 | 1 | 11 |
| 3 | 1 | 12 |
| 4 | 2 | 20 |
| 5 | 2 | 21 |
| 6 | 2 | 22 |
+-----------------+------+---------------+
Really need your help guys, for better query..

MySQL SUM() returning spurious (?) values

Despite spending an hour on this, the solution is eluding me still. I have a complex-ish query that is returning incorrect data for the SUM(). Yet, when I strip it down to the barest form, it outputs the correct data. But why and fix, I cannot figure out.
The Problem
SELECT po.*, SUM( poo.material_qty ) AS total_items_ordered, suppliers.supplier_name
FROM `purchase_orders` po
LEFT JOIN purchase_orders_items poo ON poo.poid = po.poid
LEFT JOIN suppliers ON suppliers.supplier_id = po.supplier_id
LEFT JOIN materials_batch mb ON mb.purchase_order_no = po.poid
WHERE po_status NOT
IN (
'Fulfilled', 'Cancelled'
)
AND batch_status NOT
IN (
'Arrived', 'Cancelled', 'Refused', 'Missing', 'Damaged', 'Completed'
)
GROUP BY po.poid
ORDER BY date_expected ASC
Provides wildly incorrect data for 'total_items_ordered'.
+-------+---------------------+---------------------+-------------+--------+-------------+--------+-----------+---------+----------+--------+----+--------+-----------+---------------------+-----------------------+
| poid | date_raised | date_expected | supplier_id | job_id | job_item_id | ref_no | sub_total | VAT | total | userid | DN | manual | po_status | total_items_ordered | supplier_name |
+-------+---------------------+---------------------+-------------+--------+-------------+--------+-----------+---------+----------+--------+----+--------+-----------+---------------------+-----------------------+
| 15571 | 2014-06-24 13:32:55 | 2014-06-25 00:00:00 | 1 | 0 | 0 | | 14850.10 | 2970.02 | 17820.12 | 1 | | N | Raised | 545 | John Parker & Son Ltd |
| 15572 | 2014-06-24 13:33:26 | 2014-06-25 00:00:00 | 1 | 0 | 0 | | 997.80 | 199.56 | 1197.36 | 1 | | N | Raised | 80 | John Parker & Son Ltd |
+-------+---------------------+---------------------+-------------+--------+-------------+--------+-----------+---------+----------+--------+----+--------+-----------+---------------------+-----------------------+
2 rows in set (0.00 sec)
And yet, when I strip all the complexities out of the query and run the raw SUM(), the value is correct:
mysql> SELECT poid, SUM(material_qty) AS total_items_ordered FROM `purchase_orders_items` GROUP BY poid;
+-------+---------------------+
| poid | total_items_ordered |
+-------+---------------------+
| 15571 | 109 |
| 15572 | 20 |
+-------+---------------------+
2 rows in set (0.00 sec)
Can anyone shed any light on where I'm going wrong here?? I've included all the test table content below just in case you can spot something I've missed. Thank you!
Data Example
mysql> SELECT * FROM purchase_orders;
+-------+---------------------+---------------------+-------------+--------+-------------+--------+-----------+---------+----------+--------+----+--------+-----------+
| poid | date_raised | date_expected | supplier_id | job_id | job_item_id | ref_no | sub_total | VAT | total | userid | DN | manual | po_status |
+-------+---------------------+---------------------+-------------+--------+-------------+--------+-----------+---------+----------+--------+----+--------+-----------+
| 15571 | 2014-06-24 13:32:55 | 2014-06-25 00:00:00 | 1 | 0 | 0 | | 14850.10 | 2970.02 | 17820.12 | 1 | | N | Raised |
| 15572 | 2014-06-24 13:33:26 | 2014-06-25 00:00:00 | 1 | 0 | 0 | | 997.80 | 199.56 | 1197.36 | 1 | | N | Raised |
+-------+---------------------+---------------------+-------------+--------+-------------+--------+-----------+---------+----------+--------+----+--------+-----------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM purchase_orders_items;
+--------+-------+-------------+--------------+----------------+--------------+--------------------------------------------------+
| poi_id | poid | material_id | material_qty | material_price | material_sku | material_name |
+--------+-------+-------------+--------------+----------------+--------------+--------------------------------------------------+
| 1 | 15571 | 2 | 3 | 100.00 | PKS275282 | 406x140 White Universal Beam (S355) |
| 2 | 15571 | 5 | 10 | 17.40 | 118-64-44 | Test Item (S275) |
| 3 | 15571 | 8 | 1 | 9984.50 | 113-64-21 | A really really really big universal beam (S355) |
| 4 | 15571 | 9 | 77 | 10.00 | 12345 | A thing |
| 5 | 15571 | 10 | 18 | 201.20 | 12-34-56 | 102x230 Narrow Beam (S355) |
| 6 | 15572 | 2 | 6 | 100.00 | PKS275282 | 406x140 White Universal Beam (S355) |
| 7 | 15572 | 5 | 9 | 17.40 | 118-64-44 | Test Item (S275) |
| 8 | 15572 | 9 | 4 | 10.00 | 12345 | A thing |
| 9 | 15572 | 10 | 1 | 201.20 | 12-34-56 | 102x230 Narrow Beam (S355) |
+--------+-------+-------------+--------------+----------------+--------------+--------------------------------------------------+
9 rows in set (0.00 sec)
mysql> SELECT * FROM suppliers;
+-------------+-----------------------+--------------------+--------------+---------------------+-------------------+-----------------------+--------------------------+---------------------+----------------------+
| supplier_id | supplier_name | supplier_telephone | supplier_fax | supplier_added_date | supplier_added_by | supplier_last_updated | supplier_last_updated_by | supplier_assessed | supplier_approved_by |
+-------------+-----------------------+--------------------+--------------+---------------------+-------------------+-----------------------+--------------------------+---------------------+----------------------+
| 1 | John Parker & Son Ltd | 01227 783333 | 0800 521932 | 2014-05-04 15:57:43 | 1 | 2014-06-05 16:38:23 | 1 | 2014-05-04 15:57:43 | 2 |
| 2 | Superior Glass Ltd. | 01825 764766 | 01825 767699 | 2014-05-04 17:48:38 | 1 | 2014-06-04 20:14:16 | 1 | 2014-05-04 17:48:38 | 3 |
| 3 | DTS Origins Ltd. | 01283 3283029 | 01928 303494 | 2014-05-04 17:51:57 | 1 | 2014-05-04 17:53:08 | 1 | 2014-05-04 17:51:57 | 2 |
+-------------+-----------------------+--------------------+--------------+---------------------+-------------------+-----------------------+--------------------------+---------------------+----------------------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM materials_batch;
+-------------------+-------+---------------------+-------------------+------------------+-----+---------+------------+-------------+-------------+--------------+
| material_batch_id | poiid | rcvd_date | purchase_order_no | delivery_note_no | qty | rcvd_by | dn_scanned | material_id | supplier_id | batch_status |
+-------------------+-------+---------------------+-------------------+------------------+-----+---------+------------+-------------+-------------+--------------+
| 1 | 1 | 0000-00-00 00:00:00 | 15571 | | 3 | 0 | No | 2 | 1 | Ordered |
| 2 | 2 | 0000-00-00 00:00:00 | 15571 | | 10 | 0 | No | 5 | 1 | Ordered |
| 3 | 3 | 0000-00-00 00:00:00 | 15571 | | 1 | 0 | No | 8 | 1 | Ordered |
| 4 | 4 | 0000-00-00 00:00:00 | 15571 | | 77 | 0 | No | 9 | 1 | Ordered |
| 5 | 5 | 0000-00-00 00:00:00 | 15571 | | 18 | 0 | No | 10 | 1 | Ordered |
| 6 | 6 | 0000-00-00 00:00:00 | 15572 | | 6 | 0 | No | 2 | 1 | Ordered |
| 7 | 7 | 0000-00-00 00:00:00 | 15572 | | 9 | 0 | No | 5 | 1 | Ordered |
| 8 | 8 | 0000-00-00 00:00:00 | 15572 | | 4 | 0 | No | 9 | 1 | Ordered |
| 9 | 9 | 0000-00-00 00:00:00 | 15572 | | 1 | 0 | No | 10 | 1 | Ordered |
+-------------------+-------+---------------------+-------------------+------------------+-----+---------+------------+-------------+-------------+--------------+
The reason for the wrong results should be clear when you leave out the GROUP BY from your query. For each table you JOIN, the number of returned rows is multiplied by the number of rows found by the JOIN.
As the materials_batch table contains multiple entries per order, the resulting total_items_ordered is multiplied by 5 for order number 15571, and its multiplied by 4 for order number 15572.
Try the following:
SELECT
po.*,
(
SELECT SUM(poo.material_qty)
FROM purchase_orders_items poo
WHERE poo.poid = po.poid
) AS total_items_ordered,
suppliers.supplier_name
FROM `purchase_orders` po
LEFT JOIN suppliers ON suppliers.supplier_id = po.supplier_id
LEFT JOIN materials_batch mb ON mb.purchase_order_no = po.poid
WHERE po_status NOT
IN (
'Fulfilled', 'Cancelled'
)
AND batch_status NOT
IN (
'Arrived', 'Cancelled', 'Refused', 'Missing', 'Damaged', 'Completed'
)
GROUP BY po.poid
ORDER BY date_expected ASC