SQL 计算 'reserved' 个库存项目的查询

SQL Query to calculates 'reserved' inventory items

我们正在为称为 readoutprobes 和 readoutprobekits 的项目创建库存系统。下面的模式被简化,使用 itemsitemkits.

成套物品是 1 个或多个物品的预定义集合,即成套物品。在套件中,特定类型的物品只能出现一次。一个套件通常包含约 40 件物品。套件中项目的定义,由 itemkit_item table 捕获。套件的库存在 itemkit_containers table.

中捕获

itemkit_container跟踪物理项目容器。相反,它假设物理物品包是正确的 'assembled',使用一组物理项目,但我们不知道是哪些。填充后,itemkit_containers 记录中的 'populated' 字段设置为 true。

物品 的库存由 item_containers table 跟踪。它的存在由容器卷监控。当体积为0时,容器被认为是空的。

从 item_container table 中获取特定物品的体积 > 0 的物理物品容器的数量,同样适用于套件

我们想为每个项目获得一个 'reserved count' 编号,以反映套件库存。

例如,假设我们有一个名为 A 的物品,其计数为 42。如果我们正在创建一个包含名为 A 的物品和对应的 itemkit_container 的物品包,我们希望有一个计数'reserved' 为 1,对于项目 A。

项目的 'master query' 如下所示:

SELECT items.*,         
    ic.item_count
FROM items
LEFT JOIN (
    SELECT p.id, COUNT(*) item_count, ic.item_id
    FROM  items AS p, item_containers AS ic
    WHERE p.id = ic.item_id AND ic.volume > 0
    GROUP BY p.id
    ) AS ic   
    ON ic.item_id = items.id        
GROUP BY items.id    
ORDER BY items.id;

项目中的数据 table:

item_containers中的数据table:

物品包中的数据table:

itemkit_item中的数据table:

而itemkit_containers中的数据:

可以观察到,物品包及其库存的唯一记录包含物品 ID = {1,3}

这道题是想知道如何查询'free'(或预留)实体物品的数量,即有item_containers库存,在任何一个时间点。

上面的查询,returns这个结果:

我们需要一个附加字段,指示每个项目的 'Reserved' 计数,反映项目和项目包的实际库存状态。

对于上面的数据,这将是

A -> Reserved = 1
B -> Reserved = 0
C -> Reserved = 1
D -> Reserved = 0

创建并填充上述 table 的数据库 fiddle 位于此处: DB Fiddle

我们正在使用 MySQL 8.0.

注意:以下答案接近正确。但是,它不会将 item_containers(实际库存)与 itemkit_container 记录相关联,而是将 itemkit 记录相关联。通过将 itemkit_containers table 中的填充字段切换为“0”,这一点变得很清楚。即:

即使不再填充套件,输出也会显示相同的 'Reserved' 计数。在这种情况下,保留应等于“0”。 这是针对这种情况的 fiddle:Fiddle where Reserved should be all '0'

感谢如此详细的描述和所有必要的示例数据。

正如您已经在查询中尝试过的那样,您可以通过加入项目和 item_containers table 来获得具有数量的项目。为了计算免费或保留的物品,您需要 left join itemkit_containsers table 因为套件中物品的库存存储在那里。因此,只需计算 itemkit_containers 中任何商品的数量,然后您就可以得到您的预留数量,然后从 item_containsers table 的 item_count 中减去它,即可为您提供该商品的免费数量。

架构和插入语句:

 CREATE TABLE `items` (
   `id` int NOT NULL AUTO_INCREMENT,
   `name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'oligoname + fluorophore wavelength',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=1006 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='ReadoutProbes for mFISH Survey';
 
 CREATE TABLE `item_containers` (
   `id` int NOT NULL AUTO_INCREMENT,
   `item_id` int NOT NULL COMMENT 'content of tube',
   `volume` float(12,2) NOT NULL COMMENT 'volume in micro liter (uL)',
   PRIMARY KEY (`id`),
   KEY `fk_item_containers_items` (`item_id`),
   CONSTRAINT `fk_item_containers_items` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=764 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Physical tubes received from vendor';
 
 CREATE TABLE `itemkits` (
   `id` int NOT NULL AUTO_INCREMENT,
   `name` varchar(100) DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `name` (`name`),
   UNIQUE KEY `Unique` (`name`)
 ) ENGINE=InnoDB AUTO_INCREMENT=1030 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='A readout kit is a collection of readouts, and defined in a codebook';
 
 CREATE TABLE `itemkit_containers` (
   `id` int NOT NULL AUTO_INCREMENT,
   `itemkit_id` int NOT NULL,
   `populated` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Field used for checking in checking out a tray',
   PRIMARY KEY (`id`),
   KEY `fk_readoutkit_tray_readoutkits` (`itemkit_id`),
   CONSTRAINT `fk_readoutkit_tray_readoutkits` FOREIGN KEY (`itemkit_id`) REFERENCES `itemkits` (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=1027 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Physical readoutkit_tray';
 
 CREATE TABLE `itemkit_item` (
   `itemkit_id` int NOT NULL,
   `item_id` int NOT NULL,
   UNIQUE KEY `Uniqueness` (`itemkit_id`,`item_id`),
   KEY `fk_readoutkit_item_readout_probes` (`item_id`),
   CONSTRAINT `fk_readoutkit_item_readout_probes` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`),
   CONSTRAINT `fk_readoutkit_item_readoutkits` FOREIGN KEY (`itemkit_id`) REFERENCES `itemkits` (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='associations table for definition of a readout kit';
       
 insert  into `items`(`id`,`name`) values 
 (1,'A'),
 (2,'B'),
 (3,'C'),
 (4,'D');
 
 insert  into `itemkits`(`id`,`name`) values 
 (1,'Kit_1');
 
 insert  into `itemkit_containers`(`itemkit_id`,`populated`) values 
 (1,0);
 
 insert  into `itemkit_item`(`itemkit_id`,`item_id`) values 
 (1,1),
 (1,3);
 
 insert  into `item_containers`(`item_id`,`volume`) values 
 (1,1.00),
 (2,1.00),
 (3,1.00),
 (4,1.00),
 (1,1.00);
 

查询:

select i.id,i.name,sum(ic.volume) as total_volume,
      sum(coalesce(ii.item_count,0)) as Reserved 
      from items i inner join item_containers ic on i.id=ic.item_id
      left join (select item_id,count(*) as item_count from itemkit_containers ic
      inner join itemkit_item i on ic.itemkit_id =i.itemkit_id and ic.populated=1
      group by item_id) ii
      on i.id=ii.item_id
      group by i.id,i.name
      order by i.id,i.name

输出:

id name total_volume Reserved
1 A 2.00 0
2 B 1.00 0
3 C 1.00 0
4 D 1.00 0

dbhere

Db-Fiddle 填充和未填充 itemkit_containsers:

Select 查询(示例数据):

 SELECT * from items;
 SELECT item_id, volume from item_containers;
 SELECT * FROM itemkits;
 SELECT itemkit_id, populated FROM itemkit_containers;
 SELECT * FROM itemkit_item;

输出:

id name
1 A
2 B
3 C
4 D
item_id volume
1 1.00
2 1.00
3 1.00
4 1.00
1 1.00
id name
1 Kit_1
2 Kit_2
itemkit_id populated
1 0
2 1
itemkit_id item_id
1 1
2 2
1 3

查询:

      select i.id,i.name,sum(ic.volume) as total_volume,
      sum(coalesce(ii.item_count,0)) as Reserved 
      from items i inner join item_containers ic on i.id=ic.item_id
      left join (select item_id,count(*) as item_count from itemkit_containers ic
      inner join itemkit_item i on ic.itemkit_id =i.itemkit_id and ic.populated=1
      group by item_id) ii
      on i.id=ii.item_id
      group by i.id,i.name
      order by i.id,i.name

输出:

id name total_volume Reserved
1 A 2.00 0
2 B 1.00 1
3 C 1.00 0
4 D 1.00 0

dbhere

添加了考虑 populated column of itemkit_containers 并为 reserved counts.

提供正确输出的 sql 语句

查询:

SELECT items.*,         
    ic.*,
    v.total_volume,
    COALESCE(item_in_kit.item_count,0) AS Reserved
FROM items
LEFT JOIN (
    SELECT i.id, COUNT(*) item_count, ic.item_id
    FROM  items AS i, item_containers AS ic
    WHERE i.id = ic.item_id AND ic.volume > 0
    GROUP BY i.id
    ) AS ic   
    ON ic.item_id = items.id        
    
LEFT JOIN (
    SELECT items.id, COALESCE(SUM(ic.volume),0) total_volume
    FROM items, item_containers AS ic
    WHERE items.id = ic.item_id
    GROUP BY items.id
    ) AS v
    ON items.id = v.id
    
LEFT JOIN item_containers 
    ON item_containers.item_id = items.id
    
LEFT JOIN (
    SELECT item_id, COUNT(*) AS item_count 
    FROM itemkit_item where itemkit_id not in
    (select itemkit_id from itemkit_containers where populated = 0)
    GROUP BY item_id
    ) item_in_kit
    ON items.id = item_in_kit.item_id        
    
GROUP BY items.id    
ORDER BY items.id;

输出:

id name id item_count item_id total_volume Reserved
1 A 1 2 1 2.00 0
2 B 2 1 2 1.00 0
3 C 3 1 3 1.00 0
4 D 4 1 4 1.00 0

Fiddle with correct reserved

首先计算总数并从适当的容器中保留,然后离开连接总数并保留到项目。

SELECT items.id, items.name,
    coalesce(total.v, 0) AS Total_volume,
    coalesce(rsvd.v, 0) AS Reserved
FROM items
LEFT JOIN ( /* item total in item_containers  */
       SELECT item_id, SUM(volume) v
       FROM item_containers 
       GROUP BY item_id
    ) AS total ON items.id = total.item_id
LEFT JOIN( /* item reservation by itemkit_containers  */
       SELECT iki.item_id, count(*) v
       FROM itemkit_containers AS ic
       JOIN itemkit_item AS iki
       ON ic.itemkit_id = iki.itemkit_id AND ic.Populated = 1
       GROUP BY iki.item_id 
    ) AS rsvd ON items.id = rsvd.item_id
ORDER BY items.id;

db-fiddle