Running total by item and week - mysql

I am trying to determine what my inventory level for each product is at the end of each business week.
I have a table that records all product transactions
+------------+-------------+------------+------------+
| ProductID | Quantity | Created_at |Cause |
+------------+-------------+------------+------------+
| 1 | 200 | 2015-06-01 |Delivery |
| 1 | -2 | 2015-06-02 |Order |
| 1 | -1 | 2015-06-12 |Order |
| 2 | 45 | 2015-06-15 |Delivery |
| 2 | -5 | 2015-06-16 |Order |
| 2 | -1 | 2015-06-17 |Broken |
| 1 | 100 | 2015-06-21 |Delivery |
+------------+-------------+------------+------------+
I would need to sum the quantity only up to the particular date, by product, to show something like this
+------------+-------------+------------+
| ProductID | Quantity | Week |
+------------+-------------+------------+
| 1 | 198 | 2015-06-05 |
| 1 | 197 | 2015-06-12 |
| 1 | 197 | 2015-06-19 |
| 2 | 39 | 2015-06-19 |
| 1 | 297 | 2015-06-26 |
| 2 | 39 | 2015-06-26 |
+------------+-------------+------------+
I have tried various combinations of With Rollup and #runtot:=
But have not been successful so far.

So, something like:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(product_id INT NOT NULL
,created_at DATE NOT NULL
,quantity INT NOT NULL
,cause VARCHAR(20) NOT NULL
,PRIMARY KEY(product_id,created_at)
);
INSERT INTO my_table VALUES
(1,'2015-06-01',200,'Delivery'),
(1,'2015-06-02', -2,'Order'),
(1,'2015-06-12', -1,'Order'),
(2,'2015-06-15', 45,'Delivery'),
(2,'2015-06-16', -5,'Order'),
(2,'2015-06-17', -1,'Broken'),
(1,'2015-06-21',100,'Delivery');
SELECT x.*
, CASE WHEN #prev = product_id THEN #i:=#i+total ELSE #i:=total END running
, #prev:=product_id prev
FROM
( SELECT product_id
, YEARWEEK(created_at) yw
, SUM(quantity) total
FROM my_table
GROUP
BY product_id
, yw
) x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY product_id
, yw;
+------------+--------+-------+---------+------+
| product_id | yw | total | running | prev |
+------------+--------+-------+---------+------+
| 1 | 201522 | 198 | 198 | 1 |
| 1 | 201523 | -1 | 197 | 1 |
| 1 | 201525 | 100 | 297 | 1 |
| 2 | 201524 | 39 | 39 | 2 |
+------------+--------+-------+---------+------+

Related

How to deduct sold quantity from stock tables in MySQL

I have two mysql tables. The two tables with the sample data are as follows.
select * from stock;
+----------+----------+--------+---------+-------------------+---------------+-------------+---------------------+
| stock_id | qty_type | qty | item_id | stock_location_id | stock_type_id | purchase_id | created_date |
+----------+----------+--------+---------+-------------------+---------------+-------------+---------------------+
| 48 | v | 44.00 | 1 | 1 | 1 | 38 | 2022-05-16 14:27:19 |
| 49 | v | 8.00 | 263 | 1 | 1 | 38 | 2022-05-16 14:27:19 |
| 50 | a | 6.00 | 1 | 1 | 1 | 39 | 2022-05-16 14:30:04 |
| 51 | a | 4.00 | 263 | 1 | 1 | 39 | 2022-05-16 14:30:04 |
| 56 | a | 28.00 | 1 | 1 | 1 | 41 | 2022-05-16 14:51:59 |
| 57 | a | 57.00 | 263 | 1 | 1 | 41 | 2022-05-16 14:51:59 |
| 58 | a | 6.00 | 264 | 1 | 1 | 41 | 2022-05-16 14:51:59 |
| 59 | a | 19.00 | 301 | 1 | 1 | 41 | 2022-05-16 14:51:59 |
| 64 | a | 15.00 | 263 | 1 | 5 | 0 | 2022-05-18 17:23:37 |
| 65 | a | 546.00 | 264 | 1 | 5 | 0 | 2022-05-18 17:23:37 |
| 66 | a | 15.00 | 263 | 1 | 5 | 0 | 2022-05-18 17:24:21 |
| 67 | a | 546.00 | 264 | 1 | 5 | 0 | 2022-05-18 17:24:21 |
| 72 | v | 20.00 | 720 | 1 | 1 | 44 | 2022-05-24 09:24:43 |
| 73 | v | 2.00 | 729 | 1 | 1 | 44 | 2022-05-24 09:24:43 |
+----------+----------+--------+---------+-------------------+---------------+-------------+---------------------+
select * from sales;
+----------+---------+----------+-----------+
| sales_id | item_id | quantity | basket_id |
+----------+---------+----------+-----------+
| 7 | 1 | 20.00 | 4 |
| 8 | 263 | 3.00 | 4 |
| 9 | 1 | 2.00 | 5 |
| 10 | 263 | 4.00 | 5 |
| 11 | 264 | 6.00 | 5 |
| 12 | 301 | 1.00 | 5 |
+----------+---------+----------+-----------+
By this I want to build up a update query. This should deduct the quantity of the items in the sales table from the stock table. If such a query can be created in mysql it should be updated in ascending order on the stock_id in the stock table.
If such an update query can be built, based on the data in the two tables above, I expect the result to be as follows.
select stock_id, qty_type, qty, item_id from stock where qty_type = 'a';
+----------+----------+--------+---------+
| stock_id | qty_type | qty | item_id |
+----------+----------+--------+---------+
| 50 | a | 0.00 | 1 | -- clear by sales
| 51 | a | 0.00 | 263 | -- clear by sales
| 56 | a | 12.00 | 1 | -- deduct qty by sales
| 57 | a | 54.00 | 263 | -- deduct qty by sales
| 58 | a | 0.00 | 264 | -- clear by sales
| 59 | a | 18.00 | 301 | -- deduct qty by sales
| 64 | a | 15.00 | 263 |
| 65 | a | 546.00 | 264 |
| 66 | a | 15.00 | 263 |
| 67 | a | 546.00 | 264 |
+----------+----------+--------+---------+
Any help would be highly appreciated.
Here is my understanding of the problem:
According to your stock table, there are 06 quantity in hand of item_id #1
+----------+----------+-------+---------+
| stock_id | qty_type | qty | item_id |
+----------+----------+-------+---------+
| 50 | a | 6.00 | 1 |
+----------+----------+-------+---------+
Then again a new stock comes in and new entry is made in stock table. For example, another 28 quantity has come in, so the table would look like as follows:
+----------+----------+-------+---------+
| stock_id | qty_type | qty | item_id |
+----------+----------+-------+---------+
| 50 | a | 6.00 | 1 |
| 56 | a | 28.00 | 1 |
+----------+----------+-------+---------+
Now, there are 6 + 28 = 34 quantity in stock. According to your sales data, you have sold twice of item_id #1. As first time 20 items and next time 2 items. Since we have more quantity in stock than sold quantity, so we can do both sales.
As you mentioned in your question, now we have to adjust the sold items from stock in ascending order on the stock_id in the stock table. Of course here you are using the First In First Out (FIFO) method to maintain your stock. This method works in such a way that you have to clear stock which has come first.
Now it would not be a very simple SQL which can update the stock in hand, however this is my attempt.
INSERT INTO stock (stock_id, qty)
SELECT stock_id
, newqty
FROM (
SELECT
stock_id
, #sales := IF(#prev = k.item_id, #sales, sold) as sales
, IF(qty <= #sales, 0, qty - #sales) as newqty
, #sales := IF(#sales >= qty, #sales - qty, 0)
, #prev := k.item_id as item
FROM stock k
JOIN (
SELECT item_id
, sum(quantity) as sold
FROM sales
GROUP BY item_id
) s ON k.item_id = s.item_id AND qty_type = 'a'
JOIN (SELECT #prev:=null, #sales:=0) init
ORDER BY k.item_id, stock_id
) calc
ON DUPLICATE KEY UPDATE
qty = VALUES(qty);
My query can only be run once for a set of sales data as each time it reduces the stock and updates the stock table (as you requested). Not the best method as it means storing derived data.
The following is the result of running the above query one at a time each time data is added to the sales table.
Starting Stock
select * from stock;
+----------+----------+-------+---------+-------------------+---------------+-------------+
| stock_id | qty_type | qty | item_id | stock_location_id | stock_type_id | purchase_id |
+----------+----------+-------+---------+-------------------+---------------+-------------+
| 1 | a | 6.00 | 1 | 1 | 5 | 0 |
| 2 | a | 28.00 | 1 | 1 | 5 | 0 |
+----------+----------+-------+---------+-------------------+---------------+-------------+
Result 01 - After 1st sale
select * from sales;
+----------+---------+----------+-----------+
| sales_id | item_id | quantity | basket_id |
+----------+---------+----------+-----------+
| 1 | 1 | 20.00 | 1 |
+----------+---------+----------+-----------+
select * from stock;
+----------+----------+-------+---------+-------------------+---------------+-------------+
| stock_id | qty_type | qty | item_id | stock_location_id | stock_type_id | purchase_id |
+----------+----------+-------+---------+-------------------+---------------+-------------+
| 1 | a | 0.00 | 1 | 1 | 5 | 0 |
| 2 | a | 14.00 | 1 | 1 | 5 | 0 |
+----------+----------+-------+---------+-------------------+---------------+-------------+
Result 02 - After 2nd sale
select * from sales;
+----------+---------+----------+-----------+
| sales_id | item_id | quantity | basket_id |
+----------+---------+----------+-----------+
| 1 | 1 | 20.00 | 1 |
| 2 | 1 | 2.00 | 2 |
+----------+---------+----------+-----------+
select * from stock;
+----------+----------+-------+---------+-------------------+---------------+-------------+
| stock_id | qty_type | qty | item_id | stock_location_id | stock_type_id | purchase_id |
+----------+----------+-------+---------+-------------------+---------------+-------------+
| 1 | a | 0.00 | 1 | 1 | 5 | 0 |
| 2 | a | 12.00 | 1 | 1 | 5 | 0 |
+----------+----------+-------+---------+-------------------+---------------+-------------+

Calculate periods and average sum where balance was positive

I'm stuck with window function.
I have this table called task:
user_id VARCHAR
date DATE
balance INTEGER
+---------+------------+---------+
| user_id | date | balance |
+---------+------------+---------+
| 1 | 03.04.2020 | 0 |
| 1 | 04.04.2020 | 265 |
| 1 | 05.04.2020 | 140 |
| 1 | 06.04.2020 | 70 |
| 1 | 07.04.2020 | 0 |
| 2 | 03.04.2020 | 535 |
| 2 | 04.04.2020 | 115 |
| 2 | 05.04.2020 | 0 |
| 2 | 06.04.2020 | 0 |
| 2 | 07.04.2020 | 694 |
+---------+------------+---------+
I'm trying to calculate all the periods where balance was constantly positive.
So the output table should look like this:
+---------+------------+------------+-------------+-------------+
| user_id | start_date | end_date | avg_balance | date_length |
+---------+------------+------------+-------------+-------------+
| 1 | 04.04.2020 | 06.04.2020 | 158.3 | 3 |
| 2 | 03.04.2020 | 04.04.2020 | 325 | 2 |
| 2 | 07.04.2020 | 07.04.2020 | 694 | 1 |
+---------+------------+------------+-------------+-------------+
I've tried to implement the window function but got stuck.
Assign periods by counting the number of zeros before. Then aggregate:
select user_id, min(date), max(date), avg(balance), count(*) as date_length
from (select t.*,
sum( balance = 0 ) over (partition by user_id order by date) as grp
from t
) t
where balance > 0
group by user_id, grp;
Here is a db<>fiddle.

How to modiff my query to obtain the desired result?

I have two tables (champ_value and champ_form), i tried two queries but i didn't obtain the result that I want,
please can you give me other solution
Thanks in advance.
+-------------+-----------+---------------------+---------------+
| champ value | | | |
| | | | |
| v_id | v_value | v_fk_order_item_id | v_fk_champ_id |
| 220 | Bernad | 20000 | 1 |
| 221 | Lagaf | 20000 | 2 |
| 500 | Vincent | 20000 | 1 |
| 501 | Pernault | 20000 | 2 |
+-------------+-----------+---------------------+---------------+
+------------+-------------+---------------+-------------+
| champ_form | | | |
| cf_id | cf_position | cf_fk_form_id | cf_champ_id |
| 330 | 10 | 1800 | 1 |
| 331 | 12 | 1800 | 2 |
| 630 | 13 | 1800 | 1 |
| 631 | 14 | 1800 | 2 |
+------------+-------------+---------------+-------------+
the desired result will be like this :
+----------------+-------+-------------+-------------+----------+
| desired result | | | | |
| | | | | |
| v_id | cf_id | cf_position | cf_champ_id | v_value |
| 220 | 330 | 10 | 1 | Bernard |
| 221 | 331 | 12 | 2 | Lagaf |
| 500 | 630 | 13 | 1 | Vincent |
| 501 | 631 | 14 | 2 | Pernault |
+----------------+-------+-------------+-------------+----------+
I tried this first query :
SELECT v.v_id, cf.cf_id, cf.cf_position, cf.cf_champ_id, v.v_value
FROM champ_form cf
JOIN champ_value v ON v.v_fk_champ_id = cf.cf_champ_id
WHERE cf.cf_fk_form_id =1800
AND v.v_fk_order_item_id =20000
GROUP BY v_id
ORDER BY cf.cf_position
and I obtain this :
+---------------+-------+-------------+-------------+----------+
| group by v_id | | | | |
| | | | | |
| v_id | cf_id | cf_position | cf_champ_id | v_value |
| 220 | 330 | 10 | 1 | Bernard |
| 221 | 330 | 10 | 1 | Vincent |
| 500 | 331 | 12 | 2 | Lagaf |
| 501 | 331 | 12 | 2 | Pernault |
+---------------+-------+-------------+-------------+----------+
and the second query :
SELECT v.v_id, cf.cf_id, cf.cf_position, cf.cf_champ_id, v.v_value
FROM champ_form cf
JOIN champ_value v ON v.v_fk_champ_id = cf.cf_champ_id
WHERE cf.cf_fk_form_id =1800
AND v.v_fk_order_item_id =20000
GROUP BY cf.cf_id
ORDER BY cf.cf_position
+----------------+-------+-------------+-------------+---------+
| group by cf_id | | | | |
| | | | | |
| v_id | cf_id | cf_position | cf_champ_id | v_value |
| 220 | 330 | 10 | 1 | Bernard |
| 221 | 331 | 12 | 2 | Lagaf |
| 220 | 630 | 13 | 1 | Bernard |
| 221 | 631 | 14 | 2 | Lagaf |
+----------------+-------+-------------+-------------+---------+
The first query gives the good values but not the correct positions
and the second gives the correct positions but not the correct values.
Without describing the desired behavior is difficult to understand what you want. But probably the problem is that the selected columns are not in the GROUP BY.
Try the following:
SELECT aa.v_id, cc.cf_id, cc.cf_position, cc.cf_champ_id, aa.v_value
FROM champ_value AS aa
INNER JOIN (
SELECT _aa.v_id
FROM champ_value AS _aa
INNER JOIN champ_form AS _bb
ON _aa.v_fk_champ_id = _bb.cf_champ_id
WHERE _aa.cf_fk_form_id = 1800 AND _bb.v_fk_order_item_id = 20000
GROUP BY _aa.v_id
) AS bb
ON aa.v_id = bb.v_id
INNER JOIN champ_form AS cc
ON aa.v_fk_champ_id = cc.cf_champ_id
ORDER BY aa.cf_position
More info here and here.
This does display according to position - still not convinced it's safe though!
/*
create table champ_value ( v_id int, v_value varchar(10), v_fk_order_item_id int, v_fk_champ_id int);
truncate table champ_value;
insert into champ_value values
(220 , 'Bernad' , 20000 , 1 ),
(221 , 'Lagaf' , 20000 , 2 ),
(500 , 'Vincent' , 20000 , 1 ),
(501 , 'Pernault' , 20000 , 2 );
create table champ_form(cf_id int,cf_position int, cf_fk_form_id int ,cf_champ_id int);
insert into champ_form values
(330 , 10 , 1800 , 1 ),
(331 , 12 , 1800 , 2 ),
(630 , 13 , 1800 , 1 ),
(631 , 14 , 1800 , 2 );
+----------------+-------+-------------+-------------+----------+
| desired result | | | | |
| | | | | |
| v_id | cf_id | cf_position | cf_champ_id | v_value |
| 220 | 330 | 10 | 1 | Bernard |
| 221 | 331 | 12 | 2 | Lagaf |
| 500 | 630 | 13 | 1 | Vincent |
| 501 | 631 | 14 | 2 | Pernault |
+----------------+-------+-------------+-------------+----------+
*/
SELECT s.v_id,t.cf_id, t.cf_position ,t.cf_champ_id ,s.v_value
FROM
(
SELECT I.v_id,v_value,v_fk_order_item_id,v_fk_champ_id,
#RN:=#RN + 1 RN
FROM (SELECT #RN:=0) RN,champ_value I
order by i.v_id asc
) S
LEFT OUTER JOIN
(SELECT cf_id,cf_position, cf_fk_form_id,cf_champ_id,
#RN1:=#RN1 + 1 RN1
FROM (SELECT #RN1:=0) RN1, champ_form E
order by e.cf_id asc
) T ON T.RN1 = S.RN
Actual Result
+------+-------+-------------+-------------+----------+
| v_id | cf_id | cf_position | cf_champ_id | v_value |
+------+-------+-------------+-------------+----------+
| 220 | 330 | 10 | 1 | Bernad |
| 221 | 331 | 12 | 2 | Lagaf |
| 500 | 630 | 13 | 1 | Vincent |
| 501 | 631 | 14 | 2 | Pernault |
+------+-------+-------------+-------------+----------+

Inner join on more than 2 tables

I have 3 different tables balance,received,expenses with following data in it.
Table received:
mysql> select * from received;
+-----+---------+-----------------+---------------------+
| rid | site_id | received_amount | receive_date |
+-----+---------+-----------------+---------------------+
| 1 | 1 | 500 | 2015-08-19 18:16:51 |
| 2 | 1 | 600 | 2015-08-19 18:16:52 |
| 3 | 1 | 500 | 2015-08-20 18:16:52 |
| 4 | 1 | 500 | 2015-08-19 18:16:52 |
+-----+---------+-----------------+---------------------+
Table expenses:
mysql> select * from expenses;
+-----+---------+----------------+---------------------+
| eid | site_id | expense_amount | expense_date |
+-----+---------+----------------+---------------------+
| 1 | 1 | 500 | 2015-08-19 18:17:11 |
+-----+---------+----------------+---------------------+
Table balance:
mysql> select * from balance;
+----------------+---------+---------------+--------------+-----------------+-----------------+------+------+---------------------+
| transaction_id | site_id | account_title | particulars | opening_balance | closing_balance | rid | eid | transaction_date |
+----------------+---------+---------------+--------------+-----------------+-----------------+------+------+---------------------+
| 1 | 1 | test1 | test1 values | 0 | 500 | 1 | NULL | 2015-08-19 18:16:51 |
| 2 | 1 | test1 | test1 values | 500 | 1100 | 2 | NULL | 2015-08-19 18:16:52 |
| 3 | 1 | test1 | test1 values | 1100 | 1600 | 3 | NULL | 2015-08-20 18:16:52 |
| 4 | 1 | test1 | test1 values | 1100 | 1600 | 4 | NULL | 2015-08-19 18:16:52 |
| 5 | 1 | test1 | test1 values | 1600 | 1100 | NULL | 1 | 2015-08-19 18:17:11 |
+----------------+---------+---------------+--------------+-----------------+-----------------+------+------+---------------------+
I am trying to merge the amount of received and expenses into balance table using following query but somehow I am not able to get correct way to get it.
select
b.transaction_id,
b.site_id,
b.account_title,
b.particulars,
b.opening_balance,
r.received_amount,
e.expense_amount,
b.closing_balance,
b.transaction_date
from
balance b
inner join received r
on b.site_id = r.site_id
inner join expenses e
on b.site_id = e.site_id
group by
b.transaction_id;
I am trying to get this output
+----------------+---------+---------------+--------------+-----------------+-----------------+----------------+-----------------+---------------------+
| transaction_id | site_id | account_title | particulars | opening_balance | received_amount | expense_amount | closing_balance | transaction_date |
+----------------+---------+---------------+--------------+-----------------+-----------------+----------------+-----------------+---------------------+
| 1 | 1 | test1 | test1 values | 0 | 500 | NULL | 500 | 2015-08-19 18:16:51 |
| 2 | 1 | test1 | test1 values | 500 | 600 | NULL | 1100 | 2015-08-19 18:16:52 |
| 3 | 1 | test1 | test1 values | 1100 | 500 | NULL | 1600 | 2015-08-20 18:16:52 |
| 4 | 1 | test1 | test1 values | 1600 | NULL | 500 | 1100 | 2015-08-19 18:16:52 |
| 5 | 1 | test1 | test1 values | 1100 | 500 | NULL | 1600 | 2015-08-19 18:17:11 |
+----------------+---------+---------------+--------------+-----------------+-----------------+----------------+-----------------+---------------------+
You are somewhat close. Instead of INNER JOIN, You need to do a LEFT-JOIN to the receipts and expenses. Since your "Balance" table has ALL transactions, it will drive which entry it sees within your underlying support tables. Also, don't just join on the site ID, but just the "rID" and "eID" since they would be your primary keys on the table anyhow.
No need to group by transaction ID as that is the primary key in the balance table and would only have one entry by a given ID.
select
b.transaction_id,
b.site_id,
b.account_title,
b.particulars,
b.opening_balance,
r.received_amount,
e.expense_amount,
b.closing_balance,
b.transaction_date
from
balance b
LEFT join received r
on b.rid = r.rid
AND b.site_id = r.site_id
LEFT join expenses e
on b.eid = e.eid
AND b.site_id = e.site_id
order by
b.site_id,
b.transaction_date

MySql: get data from two tables with grouping by Date

I have two table with almost same columns, i want to merge them and grouped by date
Table1: order_payments_detail
| payment_by | amount( as Debit in result) | added_on |
| Ali | 1000 | 2014-09-21 |
| Aslam | 2000 | 2014-09-25 |
| Akram | 4000 | 2014-09-28 |
Table2: orders
| cust_name | amount( as credit in result)| added_on |
| Shop1 | 1000 | 2014-09-22 |
| Shop2 | 2000 | 2014-09-26 |
| Shop3 | 4000 | 2014-09-29 |
Result will be like this
| particulars| debit | credit | added_on |
| Ali | 1000 | null | 2014-09-21 |
| Shop1 | null | 1000 | 2014-09-22 |
| Aslam | 2000 | null | 2014-09-25 |
| Shop2 | null | 2000 | 2014-09-26 |
| Akram | 4000 | null | 2014-09-28 |
| Shop3 | null | 4000 | 2014-09-29 |
You can readily do this with union all:
select payment_by, amount as debit, NULL as credit, added_on
from order_payments_detail
union all
select cust_name, NULL, amount, added_on
from orders
order by added_on;
By "grouped by date", I assume that you really mean "ordered by date".