How to deduct sold quantity from stock tables in MySQL - 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 |
+----------+----------+-------+---------+-------------------+---------------+-------------+

Related

Mysql CASE & SUM Cannot get the correct result

I have a calculation I am trying to run in the database rather than in php but I cannot get the right result out of it. It uses CASE and SUM because the calculations differ if there is a conversion factor involved.
The saved_total column is the correct total which is a stored value for comparison. The mysql_cost and mysql_total_cost columns are being calculated from the 'commodity_price' table and should match these - but are incorrect.
If I add a grouping of 'ri.preset_val' this gives me 4 rows - and the total of each pair of mysql_total_cost and mysql_cost columns would be correct if added together! (2nd table in snippet).
Tearing the little hair I have left out!
There is a Fiddle
This is the query:
SELECT ri.recipe_id,rnr.recipe_name,rnr.qty_percentage, sum(ri.cost) as saved_total,
CASE
WHEN ri.quantity_unit=3 && ri.type='uom' THEN SUM(ROUND(
cp.cost*ri.qty
,2))
WHEN ri.type='auom' THEN SUM(ROUND(
(cp.cost / 1000 * cau.conversion_factor *ri.qty)
,2))
ELSE ROUND(SUM(
(cp.cost / 1000 * ri.qty * ri.preset_val))
,2) END AS mysql_total_cost,
CASE
WHEN ri.quantity_unit=3 && ri.type='uom' THEN ROUND(SUM(
rnr.qty_percentage
*
(cp.cost*ri.qty)
*.01)
,2)
WHEN ri.type='auom' THEN ROUND(SUM(
rnr.qty_percentage
*
(cp.cost / 1000 * cau.conversion_factor *ri.qty)
*.01
),2)
ELSE ROUND(SUM(
rnr.qty_percentage
*
(cp.cost / 1000 * ri.qty * ri.preset_val)*.01)
,2) END AS mysql_cost
FROM requisition_nested_recipe rnr
LEFT JOIN recipe_ingredient ri ON ri.recipe_id=rnr.recipe_id
LEFT JOIN commodity_price cp ON ri.price_id = cp.commodity_price_id
LEFT JOIN commodity_additional_units cau ON ri.cau_id = cau.cau_id
WHERE rnr.requisition_id=1 AND ri.recipe_id=rnr.recipe_id
GROUP BY ri.recipe_id
This is the ouput:
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/><table class="table">
<tbody><tr><th>recipe_name</th><th>qty_percentage</th><th>saved_total</th><th>mysql_total_cost</th><th>mysql_cost</th></tr>
<tr><td>Brioche</td><td>33.33</td><td>1.4</td><td>1.03</td><td>0.34</td></tr>
<tr><td>Cheesecake Cream</td><td>66.67</td><td>0.95</td><td>0.94</td><td>0.63</td></tr></tbody></table><table class="table"><tbody><tr><th>recipe_name</th><th>qty_percentage</th><th>saved_total</th><th>mysql_total_cost</th><th>mysql_cost</th></tr><tr><td>Brioche</td><td>33.33</td><td>1.01</td><td>1.01</td><td>0.34</td></tr><tr><td>Brioche</td><td>33.33</td><td>0.39</td><td>0.39</td><td>0.13</td></tr><tr><td>Cheesecake Cream</td><td>66.67</td><td>0.87</td><td>0.87</td><td>0.58</td></tr><tr><td>Cheesecake Cream</td><td>66.67</td><td>0.08</td><td>0.08</td><td>0.05</td></tr></tbody></table>
So, to explain a little further, we have 2 recipes in the nested_recipe table that we need to calculate the cost of x % of (qty_percentage). Each ingredient is stored in the recipe_ingredient table. The prices of the commodities (ingredients) are stored in the commodity_price table. To complicate things some of the commodities are in different units of measure that require a conversion - which is where the commodity_additional_units table comes into play.
Note: The cost column in recipe_ingredient table is a stored value. We need to calculate it from the cost (effectively a basecost) of the commodity stored in the commodity_price table.
requisition_nested_recipe table
+----------------+-----------+------------------+----------------+
| requisition_id | recipe_id | recipe_name | qty_percentage |
+----------------+-----------+------------------+----------------+
| 1 | 3138 | Brioche | 33.33 |
| 1 | 3140 | Cheesecake Cream | 66.67 |
+----------------+-----------+------------------+----------------+
recipe_ingredient table
+-----------+--------------+--------+------+----------+--------+------------+------+
| recipe_id | commodity_id | cau_id | type | price_id | qty | preset_val | cost |
+-----------+--------------+--------+------+----------+--------+------------+------+
| 3138 | 10000012 | 0 | uom | 2152 | 350.00 | 1 | 0.27 |
| 3138 | 450526 | 0 | uom | 605 | 5.00 | 1 | 0.00 |
| 3138 | 450644 | 0 | uom | 619 | 35.00 | 1 | 0.06 |
| 3138 | 450908 | 0 | uom | 718 | 7.00 | 1 | 0.10 |
| 3138 | 300160 | 201 | auom | 499 | 3.00 | 3 | 0.39 |
| 3138 | 300021 | 0 | uom | 469 | 170.00 | 1 | 0.58 |
| 3140 | 300103 | 0 | uom | 485 | 100.00 | 1 | 0.40 |
| 3140 | 450644 | 0 | uom | 619 | 10.00 | 1 | 0.02 |
| 3140 | 450741 | 0 | uom | 629 | 0.50 | 5 | 0.08 |
| 3140 | 300104 | 0 | uom | 486 | 150.00 | 1 | 0.45 |
+-----------+--------------+--------+------+----------+--------+------------+------+
commodity_price table
+--------------------+--------------+-------------+-------+
| commodity_price_id | commodity_id | currency_id | cost |
+--------------------+--------------+-------------+-------+
| 469 | 300021 | 1 | 3.40 |
| 485 | 300103 | 1 | 4.00 |
| 486 | 300104 | 1 | 3.00 |
| 499 | 300160 | 1 | 2.25 |
| 605 | 450526 | 1 | 0.39 |
| 619 | 450644 | 1 | 1.60 |
| 629 | 450741 | 1 | 31.00 |
| 718 | 450908 | 1 | 14.83 |
| 1335 | 300021 | 2 | 4.08 |
| 1351 | 300103 | 2 | 4.80 |
| 1352 | 300104 | 2 | 3.60 |
| 1365 | 300160 | 2 | 2.70 |
| 1471 | 450526 | 2 | 0.47 |
| 1485 | 450644 | 2 | 1.92 |
| 1495 | 450741 | 2 | 37.20 |
| 1584 | 450908 | 2 | 17.80 |
| 2152 | 10000012 | 1 | 0.77 |
+--------------------+--------------+-------------+-------+
Commodity_additional_units table
+--------+--------------+------+-------------------+
| cau_id | commodity_id | auom | conversion_factor |
+--------+--------------+------+-------------------+
| 201 | 300160 | 3 | 58.00000 |
+--------+--------------+------+-------------------+
JOIN explodes the number of rows, then the SUM is done on this temp table; finally the GROUP BY shrinks the data back down.
This leads to SUM and COUNT to be bigger than they should be.
The 'fix' is to us the minimum number of JOINs when computing each aggregate. This may involve multiple subqueries.

Summarize the total of a specified item all together

I have the following table
MySQL [distributor]> select * from orderitems;
+-----------+------------+---------+----------+------------+
| order_num | order_item | prod_id | quantity | item_price |
+-----------+------------+---------+----------+------------+
| 20005 | 1 | BR01 | 100 | 5.49 |
| 20005 | 2 | BR03 | 100 | 10.99 |
| 20006 | 1 | BR01 | 20 | 5.99 |
| 20006 | 2 | BR02 | 10 | 8.99 |
| 20006 | 3 | BR03 | 10 | 11.99 |
| 20007 | 1 | BR03 | 50 | 11.49 |
| 20007 | 2 | BNBG01 | 100 | 2.99 |
| 20007 | 3 | BNBG02 | 100 | 2.99 |
| 20007 | 4 | BNBG03 | 100 | 2.99 |
| 20007 | 5 | RGAN01 | 50 | 4.49 |
| 20008 | 1 | RGAN01 | 5 | 4.99 |
| 20008 | 2 | BR03 | 5 | 11.99 |
| 20008 | 3 | BNBG01 | 10 | 3.49 |
| 20008 | 4 | BNBG02 | 10 | 3.49 |
| 20008 | 5 | BNBG03 | 10 | 3.49 |
| 20009 | 1 | BNBG01 | 250 | 2.49 |
| 20009 | 2 | BNBG02 | 250 | 2.49 |
| 20009 | 3 | BNBG03 | 250 | 2.49 |
+-----------+------------+---------+----------+------------+
18 rows in set (0.098 sec)
I am able to summarize the total price of a specified order as;
MySQL [distributor]> select order_num, sum(quantity*item_price)
as total_price from orderitems where order_num = 20008;
+-----------+-------------+
| order_num | total_price |
+-----------+-------------+
| 20008 | 189.60 |
+-----------+-------------+
1 row in set (0.026 sec)
How could I aggregate the total-price of each order and display them all together?
A SELECT statement clause that divides the query result into groups of rows, usually for the purpose of performing one or more aggregations on each group. The SELECT statement returns one row per group.
So in your all things are fine just need group by order_num
select order_num, sum(quantity*item_price) as total_price from orderitems
group by order_num
Use GROUP BY:
select order_num, sum(quantity*item_price) as total_price
from orderitems group by order_num
If you want to show total price of all orders which order details, you can remove order num from select:-
select sum(quantity*item_price) as total_price from orderitems;
However, if you are looking to show total price of all orders against each order, you can use below query :-
select order_num, sum(sum(quantity*item_price)) OVER() as total_price
from orderitems
group by order_num;

Running total by item and week

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 |
+------------+--------+-------+---------+------+

Fetch data with default rows

Suppose we have tables resource, item and price:
item
+--------+---------+
| itemId | name |
+--------+---------+
| 2743 | Product |
+--------+---------+
resource
+------------------+-----+----------------+--+
|resourceId | key | value | groupId |
+-----------+-----+----------------+---------+
| 45 | 1 | Client | 3 |
| 46 | 2 | Manufacturer | 3 |
| 69 | 1 | Delivery | 4 |
| 70 | 2 | Collection | 4 |
| 71 | 3 | Assembly | 4 |
| 72 | 4 | Client Request | 4 |
| 73 | 1 | Draft | 5 |
| 74 | 2 | Not Confirmed | 5 |
| 75 | 3 | Confirmed | 5 |
+-----------+-----+----------------+---------+
price
+---------+-----------+--------+----------+
| priceId | serviceId | itemId | price |
+---------+-----------+--------+----------+
| 294 | 4 | 0 | 20.0000 |
| 293 | 3 | 0 | 20.0000 |
| 292 | 2 | 0 | 20.0000 |
| 291 | 1 | 0 | 20.0000 |
| 290 | 1 | 2743 | 18.4000 |
| 288 | 1 | 2738 | 10.0000 |
| 267 | 4 | 2721 | 0.0000 |
| 266 | 3 | 2721 | 0.0000 |
| 265 | 2 | 2721 | 0.0000 |
+---------+-----------+--------+----------+
Rows in table resource with groupId=4 are services. They are refered to via column key from table price (serviceId). As you can see I have default prices for those services (first four rows in price with itemId=0). I need to fetch as many pirce rows as there are services for a given item. If that item doesn't have a price for a service, service's default price should be fetched. For example, the item with id = 2743 has price for service Delivery. For all other services their default prices should be returned. What I need is a result like this:
+---------+-----------+--------+----------+-----------+-----+----------------+---------+
| priceId | serviceId | itemId | price |resourceId | key | value | groupId |
+---------+-----------+--------+----------+-----------+-----+----------------+---------+
| 290 | 1 | 2743 | 18.4000 | 69 | 1 | Delivery | 4 |
| 292 | 2 | NULL | 17.0000 | 70 | 2 | Collection | 4 |
| 293 | 3 | NULL | 13.0000 | 71 | 3 | Assembly | 4 |
| 294 | 4 | NULL | 9.0000 | 72 | 4 | Client Request | 4 |
+---------+-----------+--------+----------+-----------+-----+----------------+---------+
Here is a query I tried. I get as many rows as services, but all of them have have the same price (that of the item)
SELECT *
FROM `price` AS `p`
INNER JOIN `item` ON p.id = item.id
RIGHT JOIN `resource` AS `res` ON 1 = 1
WHERE (res.groupId = 4)
AND (p.itemId = 2743);
select i.itemId, coalesce(p1.price, p0.price)
from item i
cross join price p0
left join price p1
on p1.serviceId = p0.serviceId
and p1.itemId = i.itemId
where i.itemId = 2743
and p0.itemId = 0

How can I select a column to sum total price from earlier date

+--------+---------+-------+--------+
| billID | orderId | price | date |
+--------+---------+-------+--------+
| 1 | 1 | 100 | 1.3.12 |
| 2 | 1 | 230 | 1.4.12 |
| 3 | 1 | 300 | 1.5.12 |
| 4 | 2 | 1000 | 1.3.12 |
| 5 | 2 | 160 | 1.4.12 |
| 6 | 3 | 400 | 1.3.12 |
+--------+---------+-------+--------+
I want to create view that have column that sum all price have same orderID but with date earlier than rows date. Like this:
+--------+---------+-------+--------+--------------+
| billID | orderId | price | date | add-on price |
+--------+---------+-------+--------+--------------+
| | | | | |
| 1 | 1 | 100 | 1.3.12 | 100 |
| 2 | 1 | 230 | 1.4.12 | 330 |
| 3 | 1 | 300 | 1.5.12 | 630 |
| 4 | 2 | 1000 | 1.3.12 | 1000 |
| 5 | 2 | 160 | 1.4.12 | 1160 |
| 6 | 3 | 400 | 1.3.12 | 400 |
+--------+---------+-------+--------+--------------+
You can user a correlated subquery for this:
select t.*,
(select sum(t2.price)
from table t2
where t2.orderId = t.orderId and t2.date <= t.date
) as CumulativePrice
from table t;