【问题标题】:Complex SQL Query for Inventory System库存系统的复杂 SQL 查询
【发布时间】:2019-07-31 03:22:23
【问题描述】:

我有 5 张桌子:

  1. 物品

  2. 库存

  3. ConsumedItemsMonitoring

  4. DamagedItemsMonitoring

  5. UnaccountedItems

我是复杂 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

输出看起来像是某些表被相乘了。

【问题讨论】:

  • 您遇到了什么问题?
  • 如果 Inventory 表有 ID 和 ItemID 字段,那么为什么使用 inventory.ID = Items.ItemID 为什么不使用 inventory.ItemID = Items.ItemID ?
  • 感谢您指出这一点,先生。我已经换过了。输出仍然是错误的
  • @ChetanRanpariya 输出错误先生
  • @MattPedrosa 请以文本格式分享创建表和示例数据,以便有人在本地检查并添加所需的输出/结果集。

标签: mysql sum inventory


【解决方案1】:

您所看到的是连接的工作方式以及连接在 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 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 行,我们也会为 item 1 获得 2 行

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

我们得到“双倍”消耗和损坏。

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

【讨论】:

  • 感谢先生的解释,但我对编程真的很陌生,我真的不知道该怎么做才能达到我想要的输出。我只是诚实的先生。对不起
  • @Matt Pedrosa 查看编辑。如果您想要更接近您的数据的内容,请将示例数据作为文本添加到问题中。
  • 我能够使用上面的示例创建一个有效的查询。先生非常感谢您。如果您可以创建比我创建的更好的查询,请参阅我的编辑。我觉得有一种更短的方法可以利用您的知识实现我想要的目标
  • 您可以通过使用条件聚合来改进您的答案,以避免多次加入同一个表。
【解决方案2】:

感谢@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

【讨论】:

  • 您可以通过使用条件聚合来改进您的答案,以避免多次加入同一个表,例如 LEFT JOIN (SELECT IP.ItemID, SUM(case when DATE(IP.ItemTransactionDate)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多