Getting wrong sum when using group by with inner join - mysql

With reference to this question (How to get the sum in a joined table when using group by - getting wrong results) I have two tables orders and order_items. I need to group the results by days. But I also need to get the sum of energy_used for each day from another table. When I try that using a join, I get wrong order_sum for each day (they are not being summed up). Not sure what I am doing wrong.
I would like to get for each day
the sum of order_items.energy_used for all orders created that day
the sum of orders.order_sum for all orders created that day
the created_at and order_sum that correspond to the latest order created on that day
Here is my orders table
+----+-----------+---------+---------------------+
| id | order_sum | user_id | created_at |
+----+-----------+---------+---------------------+
| 1 | 25.13 | 7 | 2020-01-25 09:13:00 |
| 2 | 10.00 | 7 | 2020-01-25 15:23:00 |
| 3 | 14.00 | 5 | 2020-01-26 10:14:00 |
| 4 | 35.00 | 1 | 2020-01-27 11:13:00 |
+----+-----------+---------+---------------------+
And here is my order_items table
+----+----------+-------------+---------------------+
| id | order_id | energy_used | created_at |
+----+----------+-------------+---------------------+
| 1 | 1 | 65 | 2020-01-25 09:13:00 |
| 2 | 1 | 12 | 2020-01-25 09:13:00 |
| 3 | 2 | 70 | 2020-01-26 10:14:00 |
| 4 | 2 | 5 | 2020-01-26 10:14:00 |
| 5 | 3 | 0 | 2020-01-27 11:13:00 |
+----+----------+-------------+---------------------+
And this is the desired result that I am trying to achieve
+---------------+-----------------+-------------------+---------------------+----------------+
| date_of_month | total_order_sum | total_energy_used | last_order_date | last_order_sum |
+---------------+-----------------+-------------------+---------------------+----------------+
| 2020-01-25 | 35.13 | 77 | 2020-01-25 09:13:00 | 25.13 |
| 2020-01-26 | 14.00 | 75 | 2020-01-26 10:14:00 | 14.00 |
| 2020-01-27 | 35.00 | 0 | 2020-01-27 11:13:00 | 35.00 |
+---------------+-----------------+-------------------+---------------------+----------------+
And here is the query that I have tried but I'm getting wrong results, the order_sum is not being calculated correctly. It is showing the same as last_order_sum
select
date(o.created_at) date_of_month,
i.total_energy_used,
o.created_at last_order_date,
o.order_sum last_order_sum,
sum(order_sum) as total_order_sum
from orders o
inner join (
select date(o1.created_at) date_of_month, sum(i1.energy_used) total_energy_used
from orders o1
inner join order_items i1 on o1.id = i1.order_id
group by date(o1.created_at)
) i on i.date_of_month = date(o.created_at)
where o.created_at = (
select max(o1.created_at)
from orders o1
where date(o1.created_at) = date(o.created_at)
)
Here is a fiddle:
https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=92b8cc2920ad9f7a7cdd56bded5a3bf2

Always join tables together on their relationships (in this case orders.id with order_items.order_id) and then group. to avoid duplicating order_sums for multiple order_items when joining, first group order_items by order_id.
select
date(o.created_at) date_of_month,
sum(i.total_energy_used),
max(o.created_at),
sum(order_sum) as total_order_sum
from orders o
inner join (
select order_id, sum(total_energy_used) total_energy_used
from order_items i
group by order_id
) i on o.id = i.order_id
group by date(o.created_at)
from this point onwards you can do a join again on orders with max(o.created_at) to get the order_sum of the last order.
moral of the story: keep an eye on your granularity.

Your problem is that you are selecting from orders, where you actually want an aggregate of orders by date. So select from two aggregtating subqueries that you join. Only problem is last_order_sum, which we can select in a further subquery, once we know the last order date.
select
order_date,
o.total_order_sum,
oi.total_energy_used,
o.last_order_date,
(
select order_sum
from orders last_order
where lastorder.created_at = o.last_order_date
) as last_order_sum
from
(
select
date(created_at) as order_date,
sum(order_sum) as total_order_sum,
max(created_date) as last_order_date
from orders
group by date(created_at)
) o
inner join
(
select
date(created_at) as order_date,
sum(energy_used) as total_energy_used
from order_items
group by date(created_at)
) oi using(order_date)
order by order_date;

What you are asking and what you show us in output are not correlated. Assuming that it is a typo:
select so.dtDay as date_of_month, so.order_sum as total_order_sum,
eu.energy_used as total_energy_used,
o.created_at as last_order_date,
o.order_sum as last_order_sum
from (
select left(created_at,10) as dtDay, sum(order_sum) as order_sum, max(id) as last_insert_id
from orders
group by left(created_at,10)
order by created_at
) so
inner join orders o on o.id = so.last_insert_id
left join (select left(created_at,10) as dtDay, sum(energy_used) as energy_used
from order_items
group by left(created_at,10)) eu on so.dtDay = eu.dtDay;
DBFiddle

Related

mysql display each day data for specified date range

Query:-Display customername,total_return in last 7 days,and quantity_sold to the customer for the last 7 days with
each day representing a column,as follows,i did till first 3 columns.
But i don't know how to display data for each particular day or date.
customername total_retuns total_sold t_6 t_5 t_4 t_3 t_2 t_1 t
---------------------------------------------------------------------------------
karan | 80 | 200 | | | | | | | |
hemanth | 90 | 300
--------------------------------------------------------------------------------
Select Query:
select c.Name,sum(od.return_quantity) totalReturns,
sum(od.quantity_sold) quantitySold from customer c
left join orders o on c.CustomerId=o.CustomerId
left join order_details od on o.OrderId=od.orderid
where o.OrderedDate between curdate()-6 and curdate()
group by c.Name order by totalreturns;

Get SUM of two fields in 2 different related tables

I'd like to get the SUM of the amount column in two related tables.
Invoices Table:
-----------------------------------------
| id | student_id | created | updated |
-----------------------------------------
| 5 | 25 | date | date |
-----------------------------------------
Invoice Items Table:
------------------------------
| id | invoice_id | amount |
------------------------------
| 1 | 5 | 250 |
------------------------------
| 2 | 5 | 100 |
------------------------------
| 3 | 5 | 40 |
------------------------------
Payments Table:
------------------------------
| id | invoice_id | amount |
------------------------------
| 1 | 5 | 100 |
------------------------------
| 2 | 5 | 290 |
------------------------------
Desired Output:
--------------------------------------
| id | invoiceTotal | paymentTotal |
--------------------------------------
| 1 | 390 | 390 |
--------------------------------------
The query I've tried
SELECT
i.id,
sum(ii.amount) as invoiceTotal,
sum(p.amount) as paymentTotal
FROM
invoices i
LEFT JOIN
invoice_items ii ON i.id = ii.invoice_id
LEFT JOIN
payments p ON i.id = p.invoice_id
WHERE
i.student_id = '25'
GROUP BY
i.id
What this seems to do is calculate the sum of the payments properly but the invoice_items.amount appears to have been duplicated by 6 (which is the number of payments there are).
I have read similar questions on SO here and here but the examples are so much more complex than what I'm trying to do and I can't figure out what to put where.
The join causes a problem with cartesian products. If a student has multiple invoice items and payments, then the totals will be wrong.
One approach that works best for all invoices is a union all/group by approach:
select i.id, sum(invoiceTotal) as invoiceTotal, sum(paymentTotal) as paymentTotal
from ((select i.id, 0 as invoiceTotal, 0 as paymentTotal
from invoices i
) union all
(select ii.invoiceId, sum(ii.amount) as invoiceTotal, NULL
from invoiceitems ii
group by ii.invoiceId
) union all
(select p.invoiceId, 0, sum(p.amount) as paymentTotal
from payments p
group by p.invoiceId
)
) iip
group by id;
For a single student, I would recommend correlated subqueries:
select i.id,
(select sum(ii.amount)
from invoiceitems ii
where ii.invoiceid = i.id
) as totalAmount,
(select sum(p.amount)
from payment p
where p.invoiceid = i.id
) as paymentAmount
from invoices i
where i.studentid = 25;

How to SUM column in MySQL left join

So i have table cont_selling
---------------------------------
cont_selling_id | date |
---------------------------------
1 | 2015-05-24 |
2 | 2015-06-06 |
---------------------------------
table 02 cont_sold
----------------------------------------------------
cont_sold_id | cont_selling_id | price |
---------------------------------------------------
1 | 1 | 10 |
2 | 1 | 10 |
3 | 1 | 30 |
4 | 2 | 20 |
5 | 2 | 10 |
--------------------------------------------------
and table 03 payment
----------------------------------------------
payment_id | cont_selling_id | paid |
-----------------------------------------------
1 | 1 | 10 |
2 | 2 | 10 |
3 | 1 | 20 |
4 | 1 | 10 |
5 | 2 | 10 |
-----------------------------------------------
now i need to SELECT table based on
now i want to merge all these three tables based on cont_selling table cont_selling_id column
and want to SUM cont_sold table price column and payment table paid column
this is what i want to do
expecting output
---------------------------------------------
cont_selling_id | price | paid |
---------------------------------------------
1 | 50 | 40 |
2 | 30 | 20 |
---------------------------------------------
so i tried like this in mysql query but it give wrong sum result
SELECT
SUM(Z.price) as total,
SUM(P.amount) as paid
FROM cont_selling S
LEFT JOIN cont_sold Z
ON S.cont_selling_id = Z.cont_selling_id
LEFT JOIN payment P
ON S.cont_selling_id = P.cont_selling_id
GROUP BY S.cont_selling_id
for this above query i m getting output like this
---------------------------------------------
cont_selling_id | price | paid |
---------------------------------------------
1 | 150 | 40 |
2 | 60 | 120 |
---------------------------------------------
Here how you can do it using the aggegare part into inner queries and then join
select
cs.cont_selling_id,
price,
paid
from cont_selling cs
left join(
select sum(price) as price , cont_selling_id from cont_sold
group by cont_selling_id
)x on x.cont_selling_id = cs.cont_selling_id,
left join(
select sum(paid) as paid , cont_selling_id from payment
group by cont_selling_id
)y
on y.cont_selling_id = cs.cont_selling_id;
You should make two different queries with SUM and then combine them to get the desired result:
SELECT T1.cont_selling_id,T1.price,T2.paid
FROM
(SELECT c.cont_selling_id,SUM(cs.price) as price
FROM cont_selling c LEFT JOIN
cont_sold cs ON c.cont_selling_id=cs.cont_selling_id
GROUP BY c.cont_selling_id) as T1 JOIN
(SELECT c.cont_selling_id,SUM(p.paid) as paid
FROM cont_selling c LEFT JOIN
payment p ON p.cont_selling_id=c.cont_selling_id
GROUP BY c.cont_selling_id) as T2 ON T1.cont_selling_id=T2.cont_selling_id
Result:
cont_selling_id price paid
----------------------------
1 50 40
2 30 20
Sample result in SQL Fiddle.
This untested query should work:
with a as(select cont_selling_id , sum(price) as totalprice from cont_sold group by cont_selling_id),
with a as(select cont_selling_id , sum(paid) as totalpaid from payment group by cont_selling_id),
select c.cont_selling_id , totalprice, totalpaid from cont_selling c left join a.count_selling_id = c.count_selling_id
left join b.count_selling_id = c.count_selling_id
You have to create temporary tables, because there is no dependency between your table for price and paid.

SQL select orders depending on status and date

I have a table order_history that is similar to the following:
+-------------------------------------------------------------+
| order_history_id | order_id | order_status_id | date_addded |
+-------------------------------------------------------------+
| 1 | 1 | 1 | 2014-03-20 |
| 2 | 1 | 2 | 2014-03-21 |
| 3 | 1 | 3 | 2014-03-29 |
| 4 | 2 | 1 | 2014-03-20 |
| 5 | 2 | 2 | 2014-03-21 |
| 6 | 2 | 3 | 2014-04-02 |
| 7 | 3 | 1 | 2014-04-20 |
| 8 | 3 | 2 | 2014-04-21 |
| 9 | 3 | 3 | 2014-04-22 |
+-------------------------------------------------------------+
The order_status represents the status of an order
+-------------------------------+
| order_status_id | name |
+-------------------------------+
| 1 | received |
| 2 | processed |
| 3 | shipped |
+-------------------------------+
what i want to do is to pull out all the orders that have been received before 2014-04-01 but not shipped until after 2014-04-01.
So in this case the query would just return order_id 2 as this is the only order that was received before 2014-04-01 yet shipped after.
I can't even seem to get started... Any help, hints, or pointers much appreciated.
You can do so ,by joining your tables and count the statues shipped for each order by using expression in sum i.e SUM(os.name ='shipped') shipped
SELECT o.*
,SUM(os.name ='shipped') shipped
FROM
orders o
LEFT JOIN orders_status os USING(order_status_id)
WHERE o.date_addded < '2014-04-01'
GROUP BY o.order_id
HAVING shipped =0
Fiddle Demo
You can use INNER JOIN with this, if I get what you really want you can try this:
SELECT DISTINCT order_id
FROM order_history A
INNER JOIN order_status B
ON A.order_status_id = B.order_status_id
WHERE (A.order_Status_id = '1' AND A.date_added < #date) AND (A.order_status_id = '3' AND A.date_added < #date)
SELECT h1.order_id
FROM order_history h1
JOIN order_status s1
ON s1.order_status_id = h1.order_status_id
JOIN order_history h2
ON h2.order_id = h1.order_id
JOIN order_status s2
ON s2.order_status_id = h2.order_status_id
WHERE h1.date_addded < '2014-04-01'
AND s1.name = 'received'
AND h2.date_addded >= '2014-04-01'
AND s2.name = 'shipped';
Note: Too many 'd's in addded
SELECT r.order_id
FROM (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'received'
AND oh.date_addded < '2014-04-01'
) AS r
JOIN (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'shipped'
AND oh.date_addded > '2014-04-01'
) AS s ON (s.order_id = r.order_id)
demo
What about this simple and light query:
SELECT DISTINCT order_id
FROM order_history o1
JOIN order_history o2
ON o1.order_id = o2.order_id
AND o1.order_status_id=1 AND o1.date_added<'2014-04-01'
AND o2.order_status_id=3 AND o2.date_added>'2014-04-01';
Not tested, but try this:
SELECT A.ORDER_ID
FROM ORDER_HISTORY A, ORDER_HISTORY B
WHERE A.ORDER_ID = B.ORDER_ID
AND A.order_status_id = 1
AND A.date_addded < TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD')
AND B.order_status_id = 3
AND B.date_addded > TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD');

MySql LEFT JOIN with COUNT

I have two tables, customers and sales. I want to count sales for each customer and create a table of sales per month for each store.
I would like to produce something like;
------------------------------
month | customers | sales |
------------------------------
1/2013 | 5 | 2 |
2/2013 | 21 | 9 |
3/2013 | 14 | 4 |
4/2013 | 9 | 3 |
but I am having trouble getting the sales count to be correct when using the following;
SELECT CONCAT(MONTH(c.added), '/', YEAR(c.added)), count(c.id), count(s.id)
FROM customers c
LEFT JOIN sales s
ON s.customer_id = c.id AND MONTH(c.added) = MONTH(s.added) AND YEAR(c.added) = YEAR(s.added)
WHERE c.store_id = 1
GROUP BY YEAR(c.added), MONTH(c.added);
Customers table;
-------------------------------
id | store_id | added |
-------------------------------
1 | 1 |2013-02-01 |
2 | 1 |2013-02-02 |
3 | 1 |2013-03-16 |
sales table;
---------------------------------
id | added | customer_id |
---------------------------------
1 | 2013-02-18 | 3 |
2 | 2013-03-02 | 2 |
3 | 2013-03-16 | 3 |
Can anyone help here?
thanks
(Updated) The existing query will only count sales made in the same month that the customer was added. Try this, instead:
SELECT CONCAT(MONTH(sq.added), '/', YEAR(sq.added)) month_year,
sum(sq.customer_count),
sum(sq.sales_count)
FROM (select s.added, 0 customer_count, 1 sales_count
from customers c
JOIN sales s ON s.customer_id = c.id
WHERE c.store_id = 1
union all
select added, 1 customer_count, 0 sales_count
from customers
WHERE store_id = 1) sq
GROUP BY YEAR(sq.added), MONTH(sq.added);
SELECT c.* , s.sales_count<br>
FROM customers c<br>
LEFT JOIN (SELECT customer_id, count(id) as sales_count FROM sales GROUP BY customer_id) s on c.id=s.customer_id<br>
WHERE c.store_id = 1<br>