MySQL Case Conditions Deduction to generate balance - mysql

I have a MySQL table named, "store_update_stock" to store purchase & issue of items. Order status column maintain the states, "purchase" or "issue" when need. Purchase quantity has denoted by plus values (eg:-10) & issue quantity has denoted by minus values (eg:- -2) in the table.
To get the purchase & issue summary I used the following query.
SELECT item_id, item_name, order_status, (CASE order_status
WHEN "issue" THEN store_update_stock_details.qty * (-1)
ELSE store_update_stock_details.qty
END) quantity FROM store_update_stock
And generated the expected output as follows :
+---------+-----------+--------------+----------+
| item_id | item_name | order_status | quantity |
+---------+-----------+--------------+----------+
| 1000 | A4 | purchase | 10 |
| 1001 | A3 | purchase | 5 |
| 1000 | A4 | issue | 2 |
| 1000 | A4 | issue | 3 |
| 1001 | A3 | purchase | 6 |
+---------+-----------+--------------+----------+
But I need to get further the balance of each items after performing purchases & issues by modifying the above query and get the output as follows :
+---------+-----------+---------+
| item_id | item_name | balance |
+---------+-----------+---------+
| 1000 | A4 | 5 |
| 1001 | A3 | 11 |
+---------+-----------+---------+
What can be done in my query to get the desired output. Can anyone helpme ?

It looks like all you might need is a simple aggregate sum and a group by:
select item_id, item_name, sum(quantity) as balance
from store_update_stock
group by item_id, item_name
maybe?

Related

How to select the sum() of a group of rows and the sum() of another group

I have created a SQLfiddle demo with sample data and desired result here :(http://sqlfiddle.com/#!9/dfe73a/7)
sample data
-- table company
+--------+---------+
| id | name |
+--------+---------+
| 1 | foo |
| 2 | bar |
+--------+---------+
-- table sales
+--------+---------------+-----------------+
| id | company_id | total_amount |
+--------+---------------+-----------------+
| 1 | 1 | 300.0 |
| 2 | 1 | 300.0 |
| 2 | 1 | 100.0 |
+--------+---------------+-----------------+
-- table moves
+--------+---------------+-----------------+
| id | company_id | balance_move |
+--------+---------------+-----------------+
| 1 | 1 | 700.0 |
| 2 | 1 | -300.0 |
| 2 | 1 | -300.0 |
+--------+---------------+-----------------+
I need to select every company along with the sum of it's total amount of sales and the sum of it's total balance moves
desired result
+----+----------------------+---------------------+
| id | total_amount_sum | balance_move_sum |
+----+----------------------+---------------------+
| 1 | 700 | 100 |
+----+----------------------+---------------------+
| 2 | (null) | (null) |
+----+----------------------+---------------------+
I tried this SQL query
SELECT
company.id,
sum(total_amount) total_amount_sum,
sum(balance_move) balance_move_sum
FROM company
LEFT JOIN sales ON company.id = sales.company_id
LEFT JOIN moves ON company.id = moves.company_id
GROUP BY company.id
But the sum() functions add all the redundant values came from the joins which result in 2100 (700*3) for total amount and 300 (100*3) for net balance
bad SQL statement result
+----+----------------------+---------------------+
| id | total_amount_sum | balance_move_sum |
+----+----------------------+---------------------+
| 1 | 2100 | 300 |
+----+----------------------+---------------------+
| 2 | (null) | (null) |
+----+----------------------+---------------------+
Is it possible to achieve the result I want ?
You're repeating rows by doing your joins.
Company: 1 row per company
After Sales join: 3 rows per company (1x3)
After Moves join: 9 rows per company (3x3)
You end up triplicating your SUM because of this.
One way to fix is to use derived tables like this, which calculate the SUM first, then join the resulting rows 1-to-1.
SELECT
company.id,
total_amount_sum,
balance_move_sum
FROM company
LEFT JOIN (SELECT SUM(total_amount) total_amount_sum, company_id
FROM sales
GROUP BY company_id
) sales ON company.id = sales.company_id
LEFT JOIN (SELECT SUM(balance_move) balance_move_sum, company_id
FROM moves
GROUP BY company_id
) moves ON company.id = moves.company_id
Using sub-queries to calculate the two sums separately will work.
SELECT
company.id,
(Select sum(total_amount) from sales where sales.company_id = company.id) total_amount_sum,
(Select sum(balance_move) from moves where moves.company_id = company.id) balance_move_sum
FROM company

using HAVING to filter results based on a reference row

I have the following table:
+---------+--------------+----------+
| item_id | location_id | price |
+---------+--------------+----------+
| 1 | 1 | 100 |
| 1 | 1 | 250 |
| 1 | 2 | 50 |
| 2 | 1 | 250 |
| 2 | 1 | 1000 |
| 3 | 1 | 1000 |
| 3 | 2 | 100 |
+---------+--------------+----------+
I can reduce this down to the minimum values using this query
SELECT
item_id, location_id, MIN(price) AS Price
from
table
GROUP BY item_id , location_id
This gets me
+---------+--------------+----------+
| item_id | location_id | price |
+---------+--------------+----------+
| 1 | 1 | 100 |
| 1 | 2 | 50 |
| 2 | 1 | 250 |
| 3 | 1 | 1000 |
| 3 | 2 | 100 |
+---------+--------------+----------+
I want to reduce this further. I am using the rows with a location_id of 1 as a reference row. For each row that has an item_id matching the reference row's item_id but a different location id. I want to compare that row's price with the reference row's price. If the price is lower than the reference row's price, I want to filter that row out.
My final result should include the reference row for each item id and any rows that met the criteria of the price being lower than the reference row price.
I have a hunch that I can use the HAVING clause to do this but I am having trouble compiling the statement. How should I construct the HAVING statement?
Thanks in advance
Nah, having can't help you like this, having is for things like you need filter min() result for something
e.g:
select id,min(price) from table where date = '2016-3-18' group by id having min(price) = 50
it will show you the records that min(price)=50
let's back to your case, there are lots of way to do that,
1. left join
select a.item_id,a.location_id,a.price
from table a
left join table b
on a.location_id = b.location_id and a.price > b.price
where b.price is null
2. exists
select a.item_id,a.location_id,a.price
from table a
where exists(
select 1 from
(select location_id,min(price)as price from table group by location_id)b
where a.location_id = b.location_id and a.price = b.price
)
normally i ll recommand you use exists

Selecting related rows in MySQL

Let me elaborate. I have a table like this (updated to include more example)
| id | date | cust | label | paid | due |
+----+-----------+------+-------------------------+------+-------+
| 1 |2016-02-02 | 1 | SALE: Acme Golf Balls | 0 | 1000 |
| 20 |2016-03-01 | 1 | PAYMENT: transaction #1 | 700 | 0 |
| 29 |2016-03-02 | 1 | PAYMENT: transaction #1 | 300 | 0 |
| 30 |2016-03-02 | 3 | SALE: Acme Large Anvil | 500 | 700 |
| 32 |2016-03-02 | 3 | PAYMENT: transaction #30| 100 | 0 |
| 33 |2016-03-03 | 2 | SALE: Acme Rockets | 0 | 2000 |
Now I need to output a table that displays sales that haven't been paid in full and the remaining amount. How do I do that? There's not much info out there on how to relate rows from the same table.
EDIT: Here's the output table I'm thinking of making
Table: debts_n_loans
| cust | label | amount |
==========================================
| 3 | SALE: Acme Large Anvil | 100 |
| 2 | SALE: Acme Rockets | 2000 |
If cust is the key that ties them together, then you can just use aggregation and a having clause:
select cust, sum(paid), sum(due)
from t
group by cust
having sum(paid) <> sum(due);
If you want the details, you can use a join, in or exists to get the details.
EDIT:
If you need to do this using the transaction at the end of the string:
select t.id, t.due, sum(tpay.paid) as paid
from t left join
t tpay
on tpay.label like '%#' || t.id
where t.label like 'SALE:%' and
tpay.label like 'PAYMENT:%'
group by t.id, t.due
having t.due <> sum(tpay.paid);
So you only need the rows with a due greater than 0
SELECT * FROM <table> WHERE due > 0;
Try this:
SELECT
cust,
SUM(due) - SUM(paid) AS remaining
FROM t1
GROUP BY cust
HAVING SUM(due) > SUM(paid);

MYSQL - Sum Rows by datetime after joining two tables

Thank you for looking at my question.
I am trying to sum the subtotal column and group by datetime via mysql. I have to join two tables, prior to the sum.
My first table, Payments, contains the following data:
ID | User_ID | Order_ID | Subtotal | Tax | Tip | Discount | Total | Payment_Method | Payment_Collected
12 | 123 | 76 | 10.99 | 0.99| 1 | 0.00 | 12.98 | Cash | 1
My second table, Order, contains the following data:
ID | User_ID | Address_ID | orderplaced_ts | order_status
76 | 23 | 123 | 2015-02-26 12:23:41 |
The query that I tried to run is as follows:
select `order`.orderplaced_ts, SUM(`payments`.subtotal) as Subtotal
from `payments`
join `order` on `order`.id=`payments`.order_id
where `order`.order_status != "Cancelled"
and `payments`.payment_collected = 1
group by `order`.orderplaced_ts
What I'm trying to achieve is to get all the sum of all subtotals and grouped by the same datetime. Sample output would be:
orderplaced_ts | Subtotal
2015-02-20 | 123.12
2015-02-21 | 223.12
2015-02-22 | 124.25
2015-02-23 | 247.23
2015-02-24 | 623.50
My current query output is:
orderplaced_ts | Subtotal
2015-02-20 05:56:23 | 123.12
2015-02-20 06:36:23 | 123.12
2015-02-20 06:38:23 | 123.12
Can someone please point me in the right direction of why my query isn't giving me the desired output?
You need to use a formatted date to get rid of the time.
I think this would do what you want (not tested):
select date_format(`order`.orderplaced_ts,"%Y-%m-%d"), SUM(`payments`.subtotal) as Subtotal
from `payments`
join `order` on `order`.id=`payments`.order_id
where `order`.order_status != "Cancelled"
and `payments`.payment_collected = 1
group by date_format(`order`.orderplaced_ts,"%Y-%m-%d")

constructing a query for the following table

can anyone generate a query for me.
Lets say i have a table sales(saleID, date_of_sales, customerID, itemID, saleprice)
date_of_sales is the datetime field which stores the time of the sale.
customerID is self exlpaining tells to whom item was sold.
itemID is ID of the item sold.
saleprice is the price that the item was sold.
I want to construct a query which will give out the detail of the last purchase by each customers. this could be done by using date_of_sales.
Example table
saleID | date_of_sales | customerID | itemID | saleprice
101 | 2008-01-01 | C2000 | I200 | 650 |
102 | 2010-01-01 | C2000 | I333 | 200 |
103 | 2007-01-01 | C3333 | I111 | 800 |
104 | 2009-12-12 | C3333 | I222 | 100 |
this is the example data table, there are only two customer for simplicity.
customer C2000 did his last purchase
on 2010-01-01
customer C3333 did his last purchase
on 2009-12-12
I want to get a result like this
customerID | date_of_sales | itemID | saleprice
C2000 | 2010-01-01 | I333 | 200 |
C3333 | 2009-12-12 | I222 | 100 |
This might be what you are looking for...
SELECT *
FROM sales
WHERE sales.date_of_sales = (SELECT MAX(date_of_sales)
FROM sales s2
WHERE s2.customerID = sales.customerID);
There is a slight problem with it; if there were two sales on the same day to the same customer, you'll get two rows (unless your date-of-sales column includes the time as well). I think the same applies to the answer above, though.
Additionally, if you DO want to get results based on only a SINGLE entry of the maximum date, I would use the query by #Sachin Shanbhag above, but add a maximum sales ID value too... Since that would be implied as sequential, whichever was entered last would probably be the most recent.
SELECT S.* FROM
sales S
INNER JOIN
( SELECT
customerID,
MAX(date_of_sales) dos,
MAX(SalesID) maxSale
FROM
sales
GROUP BY customerID
) S2 ON S.customerID = S2.customerID
AND S.date_of_sales = S2.dos
AND S.SalesID = S2.maxSale