库存系统的复杂 SQL 查询

Complex SQL Query for Inventory System

我有 5 个 table:


  1. 库存

  2. ConsumedItemsMonitoring

  3. DamagedItemsMonitoring

  4. 未计算项目

我是复杂查询的新手 SQL 做了一些研究并寻求帮助,这就是我的代码到目前为止的样子。

SELECT Items.ItemID, Items.Item, 
SUM(CASE WHEN DATE(Inventory.ItemTransactionDate) < CURDATE() THEN Inventory.Quantity ELSE 0 END) - 
SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate) <CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 end) - 
SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate)<CURDATE() THEN damagedinventory.Quantity ELSE 0 end) - 
SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate)<CURDATE() THEN unaccounteditems.Quantity ELSE 0 end) AS 'PrevBalance',

SUM(CASE WHEN DATE(Inventory.ItemTransactionDate)=CURDATE() THEN Inventory.Quantity else 0 END) AS 'DeliveredToday',

SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate)=CURDATE() THEN damagedinventory.Quantity ELSE 0 END) AS 'DamagedToday',

SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate)=CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 END) AS 'ConsumedToday',

SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate)=CURDATE() THEN unaccounteditems.Quantity ELSE 0 END) AS 'UnAccountedToday',

SUM(CASE WHEN DATE(Inventory.ItemTransactionDate) < CURDATE() THEN Inventory.Quantity else 0 end)-
SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate) < CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate) < CURDATE() THEN damagedinventory.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate) < CURDATE() THEN unaccounteditems.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate) = CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate) = CURDATE() THEN damagedinventory.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate) = CURDATE() THEN unaccounteditems.Quantity ELSE 0 END) +
SUM(CASE WHEN DATE(Inventory.ItemTransactionDate) = CURDATE() then Inventory.Quantity ELSE 0 end) AS 'Total Balance' 

FROM Items

LEFT OUTER JOIN consumeditemmonitoring ON consumeditemmonitoring.ItemID = Items.ItemID 
LEFT OUTER JOIN damagedinventory ON damagedinventory.ItemID = Items.ItemID
LEFT OUTER JOIN unaccounteditems ON unaccounteditems.ItemID = Items.ItemID
LEFT OUTER JOIN inventory ON inventory.ItemID= Items.ItemID
GROUP BY Items.ItemID

输出看起来像一些 table 相乘。

您所看到的是联接如何工作以及联接在 group by 之前执行这一事实的结果。我可以用您的数据的简化版本来说明这一点。

drop table if exists
items,
items_inventory,
items_consumed,
items_damaged,
items_unaccounted;

create table items (id int);
create table items_inventory(id int,itemid int,qty int);
create table items_consumed(id int,itemid int,qty int);
create table items_damaged(id int,itemid int,qty int);
create table items_unaccounted(id int,itemid int,qty int);

insert into items values(1),(2);
insert into items_inventory values (1,1,10),(2,1,10),(2,2,20);
insert into items_consumed values(1,1,5),(2,2,15);
insert into items_damaged values(1,1,25);

如果我们运行一个简单的select

select i.id,
         ii.id,ii.qty,
         ic.id,ic.qty,
         id.id,id.qty,
         iu.id,iu.qty
from items i
left join items_inventory   ii on ii.itemid = i.id
left join items_consumed    ic on ic.itemid = i.id
left join items_damaged     id on id.itemid = i.id
left join items_unaccounted iu on iu.itemid = i.id
;

尽管 items_consumed

只有 1 行,但我们得到 2 行的项目 1
+------+------+------+------+------+------+------+------+------+
| id   | id   | qty  | id   | qty  | id   | qty  | id   | qty  |
+------+------+------+------+------+------+------+------+------+
|    1 |    1 |   10 |    1 |    5 |    1 |   25 | NULL | NULL |
|    1 |    2 |   10 |    1 |    5 |    1 |   25 | NULL | NULL |
|    2 |    2 |   20 |    2 |   15 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+------+------+------+------+
3 rows in set (0.00 sec)

当我们聚合时

select i.id,
         count(*) as rows,
         sum(ii.qty) as inventory,
         sum(ic.qty) as consumed,
         sum(id.qty) as damaged,
         sum(iu.qty) as unaccounted
from items i
left join items_inventory   ii on ii.itemid = i.id
left join items_consumed    ic on ic.itemid = i.id
left join items_damaged     id on id.itemid = i.id
left join items_unaccounted iu on iu.itemid = i.id
group by i.id;

我们得到 'doubling' 的消耗和损坏。

+------+------+-----------+----------+---------+-------------+
| id   | rows | inventory | consumed | damaged | unaccounted |
+------+------+-----------+----------+---------+-------------+
|    1 |    2 |        20 |       10 |      50 |        NULL |
|    2 |    1 |        20 |       15 |    NULL |        NULL |
+------+------+-----------+----------+---------+-------------+
2 rows in set (0.00 sec)

处理此问题的一种方法是在您加入之前进行聚合,方法是将聚合推送到您随后要加入的子查询中。例如

select i.id, ii.inventory,ic.consumed,id.damaged,iu.unaccounted,
            coalesce(ii.inventory,0)+coalesce(ic.consumed,0)+coalesce(id.damaged,0)+coalesce(iu.unaccounted,0) total
from items i
left join (select ii.itemid,sum(ii.qty) as inventory   from items_inventory ii group by itemid)   ii on ii.itemid = i.id
left join (select ic.itemid,sum(ic.qty) as consumed    from items_consumed  ic group by itemid)   ic on ic.itemid = i.id
left join (select id.itemid,sum(id.qty) as damaged     from items_damaged   id group by itemid)   id on id.itemid = i.id
left join (select iu.itemid,sum(iu.qty) as unaccounted from items_unaccounted iu group by itemid) iu on iu.itemid = i.id
;

+------+-----------+----------+---------+-------------+-------+
| id   | inventory | consumed | damaged | unaccounted | total |
+------+-----------+----------+---------+-------------+-------+
|    1 |        20 |        5 |      25 |        NULL |    50 |
|    2 |        20 |       15 |    NULL |        NULL |    35 |
+------+-----------+----------+---------+-------------+-------+
2 rows in set (0.00 sec)

感谢 @P.Salmon 先生的工作查询

SELECT I.ItemID,
I.Item,
COALESCE(II.InventoryPrevBal,0) - COALESCE(ICP.ConsumedPrevBal,0) - COALESCE(IDP.DamagedPrevBal,0) - COALESCE(IUP.UnaccountedPrevBal,0) PrevBalance,
COALESCE(II.InventoryBal,0) CurrentDelivered,
COALESCE(IC.Consumed,0) CurrentConsumed,
COALESCE(ID.Damaged,0) CurrentDamaged,
COALESCE(IU.Unaccounted,0) CurrentUnaccounted,
COALESCE(II.InventoryPrevBal,0) +  COALESCE(II.InventoryBal,0) - COALESCE(ICP.ConsumedPrevBal,0) - COALESCE(IDP.DamagedPrevBal,0) - COALESCE(IUP.UnaccountedPrevBal,0) - COALESCE(IC.Consumed,0) - COALESCE(ID.Damaged,0) - COALESCE(IU.Unaccounted,0) CurrentTotal
FROM items I

LEFT JOIN (SELECT II.ItemID, SUM(CASE WHEN DATE(II.ItemTransactionDate) < CURDATE() THEN II.Quantity ELSE 0 END) as InventoryPrevBal, SUM(CASE WHEN DATE(II.ItemTransactionDate) = CURDATE() THEN II.Quantity ELSE 0 END) as InventoryBal FROM inventory II GROUP BY ItemID) II ON II.ItemID = I.ItemID 

LEFT JOIN (SELECT ICP.ItemID, ICP.TransactionDate, SUM(ICP.Quantity) as ConsumedPrevBal FROM consumeditemmonitoring ICP WHERE DATE(ICP.TransactionDate) < CURDATE() GROUP BY ItemID) ICP ON ICP.ItemID = I.ItemID

LEFT JOIN (SELECT IDP.ItemID, IDP.ItemTransactionDate, SUM(IDP.Quantity) as DamagedPrevBal FROM damagedinventory IDP WHERE DATE(IDP.ItemTransactionDate) < CURDATE() GROUP BY ItemID) IDP ON IDP.ItemID = I.ItemID

LEFT JOIN (SELECT IUP.ItemID, IUP.ItemTransactionDate, SUM(IUP.Quantity) as UnaccountedPrevBal FROM unaccounteditems IUP WHERE DATE(IUP.ItemTransactionDate) < CURDATE() GROUP BY ItemID) IUP ON IUP.ItemID = I.ItemID 
LEFT JOIN (SELECT IC.ItemID, IC.TransactionDate, SUM(IC.Quantity) as Consumed FROM consumeditemmonitoring IC WHERE DATE(IC.TransactionDate) = CURDATE() GROUP BY ItemID) IC ON IC.ItemID = I.ItemID
LEFT JOIN (SELECT ID.ItemID, ID.ItemTransactionDate, SUM(ID.Quantity) as Damaged FROM damagedinventory ID WHERE DATE(ID.ItemTransactionDate) = CURDATE() GROUP BY ItemID) ID ON ID.ItemID = I.ItemID
LEFT JOIN (SELECT IU.ItemID, IU.ItemTransactionDate, SUM(IU.Quantity) as Unaccounted FROM unaccounteditems IU WHERE DATE(IU.ItemTransactionDate) = CURDATE() GROUP BY ItemID) IU ON IU.ItemID = I.ItemID  
ORDER BY I.Item ASC