The following answer works by joining the sold items with the last sold items after grouping them on inventory.id and by resetting the variables when the inventory.id changes. Note how the join is on the inventory.id and group_row - 1 = group_row
This SQL example works
select sold.inventory_id
, sold.sold_id
, case sold.inventory_id when @inventoryId then
@starting := @starting
else
@starting := sold.inventory_quantity_before_sale
end as starting_quantity
, case sold.inventory_id when @inventoryId then
@runningSold := @runningSold + coalesce(last.quantity_sold,0)
else
@runningSold := 0
end as running_sold
, case sold.inventory_id when @inventoryId then
@runningInventoryQuantity := @starting - @runningSold
else
@runningInventoryQuantity := sold.inventory_quantity_before_sale
end as before_sale
, sold.quantity_sold
, sold.inventory_quantity_before_sale - sold.quantity_sold - @runningSold after_sale
, @inventoryId := sold.inventory_id group_inventory_id -- clocks over the group counter
from (
select inventorySold.*
, case inventory_id
when @inventoryId then
@groupRow := @groupRow + 1
else
@groupRow := 1
end as group_row
, @inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity_before_sale
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select @groupRow := 0, @inventoryId := 0 ) variables
order by inventory_id, sold_id
) sold
left join (
select inventorySold.*
, case inventory_id
when @inventoryId then
@groupRow := @groupRow + 1
else
@groupRow := 1
end as group_row
, @inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select @groupRow := 0, @inventoryId := 0 ) variables
order by inventory_id, sold_id
) `last`
on sold.inventory_id = `last`.inventory_id
and sold.group_row - 1 = `last`.group_row
join ( select @runningInventoryQuantity := 0, @runningSold := 0, @inventoryId := 0, @afterSold := 0, @starting :=0 ) variables
order by sold.inventory_id, sold.group_row
-- example results
inventory_id sold_id starting_quantity running_sold before_sale quantity_sold after_sale group_inventory_id
1 1 93 0 93 4 89 1
1 4 93 4 89 16 73 1
1 5 93 20 73 20 53 1
1 12 93 40 53 48 5 1
2 2 97 0 97 4 93 2
2 6 97 4 93 12 81 2
2 7 97 16 81 14 67 2
2 8 97 30 67 16 51 2
2 11 97 46 51 22 29 2
3 3 95 0 95 12 83 3
3 9 95 12 83 36 47 3
3 10 95 48 47 40 7 3
You could do the same thing in PHP. Have a starting quantity, a running amount sold and a running total that gets reset each time the inventory id changes and then use the quantity sold for each transaction to adjust those variables.
If you choose the PHP route example sql is
select sold.* from (
select inventorySold.*
, case inventory_id
when @inventoryId then
@groupRow := @groupRow + 1
else
@groupRow := 1
end as group_row
, @inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity_before_sale
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select @groupRow := 0, @inventoryId := 0 ) variables
order by inventory_id, sold_id
) sold
-- example results
inventory_id inventory_quantity_before_sale quantity_sold sold_id group_row group_inventory_id
1 93 4 1 1 1
1 93 16 4 2 1
1 93 20 5 3 1
1 93 48 12 4 1
2 97 4 2 1 2
2 97 12 6 2 2
2 97 14 7 3 2
2 97 16 8 4 2
2 97 22 11 5 2
3 95 12 3 1 3
3 95 36 9 2 3
3 95 40 10 3 3
You can get the label information by joining other tables to the result using inventory.id and sold.id.
I agree with https://stackoverflow.com/users/932820/chris-adams. If you're looking to keep a track of stocktakes over time then you'll need a transaction table to record the starting and ending inventory quantities and starting and ending timestamps ... and probably starting and ending sold ids.
-- supporting tables - run this in an empty database unless you want to destroy your current tables
DROP TABLE IF EXISTS `ws_inventory`;
CREATE TABLE `ws_inventory` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sku` varchar(20) DEFAULT NULL,
`quantity` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `ws_inventory` WRITE;
/*!40000 ALTER TABLE `ws_inventory` DISABLE KEYS */;
INSERT INTO `ws_inventory` (`id`, `sku`, `quantity`)
VALUES
(1,'WS16',93),
(2,'WS3',97),
(3,'WS6',95);
/*!40000 ALTER TABLE `ws_inventory` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table ws_mastersku
# ------------------------------------------------------------
DROP TABLE IF EXISTS `ws_mastersku`;
CREATE TABLE `ws_mastersku` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sku` varchar(20) DEFAULT NULL,
`sku1` varchar(20) DEFAULT NULL,
`sku2` varchar(20) DEFAULT NULL,
`sku3` varchar(20) DEFAULT NULL,
`multsku` tinyint(2) DEFAULT NULL,
`qtysku` int(11) DEFAULT NULL,
`altsku` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `ws_mastersku` WRITE;
/*!40000 ALTER TABLE `ws_mastersku` DISABLE KEYS */;
INSERT INTO `ws_mastersku` (`id`, `sku`, `sku1`, `sku2`, `sku3`, `multsku`, `qtysku`, `altsku`)
VALUES
(1,'WS16X4-2',NULL,NULL,NULL,NULL,4,'WS16'),
(2,'WS3X2-4',NULL,NULL,NULL,NULL,2,'WS3'),
(3,'WS6X4-16',NULL,NULL,NULL,NULL,4,'WS6');
/*!40000 ALTER TABLE `ws_mastersku` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table ws_sold
# ------------------------------------------------------------
DROP TABLE IF EXISTS `ws_sold`;
CREATE TABLE `ws_sold` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sku` varchar(20) DEFAULT NULL,
`quantity` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `ws_sold` WRITE;
/*!40000 ALTER TABLE `ws_sold` DISABLE KEYS */;
INSERT INTO `ws_sold` (`id`, `sku`, `quantity`)
VALUES
(1,'WS16X4-2',1),
(2,'WS3X2-4',2),
(3,'WS6X4-16',3),
(4,'WS16X4-2',4),
(5,'WS16X4-2',5),
(6,'WS3X2-4',6),
(7,'WS3X2-4',7),
(8,'WS3X2-4',8),
(9,'WS6X4-16',9),
(10,'WS6X4-16',10),
(11,'WS3X2-4',11),
(12,'WS16X4-2',12);
/*!40000 ALTER TABLE `ws_sold` ENABLE KEYS */;
UNLOCK TABLES;