用于计算“保留”库存项目的 SQL 查询
Posted
技术标签:
【中文标题】用于计算“保留”库存项目的 SQL 查询【英文标题】:SQL Query to calculates 'reserved' inventory items 【发布时间】:2021-07-20 07:20:02 【问题描述】:我们正在为名为 readoutprobes 和 readoutprobekits 的物品创建一个库存系统。下面的架构被简化了,使用了items和itemkits这两个词。
itemkit,是一个或多个项目的预定义集合,即一个工具包。在套件中,特定类型的物品只能出现一次。一个套件,通常包含约 40 件物品。 itemkit_item 表捕获了套件中项目的定义。套件的库存记录在 itemkit_containers 表中。
itemkit_container 不跟踪物理物品容器。相反,它假设一个物理项目包是正确“组装”的,使用一组物理项目,但我们不知道是哪些。填充后,itemkit_containers 记录中的“填充”字段设置为 true。
items 的库存由 item_containers 表跟踪。它的存在由容器卷监控。当volume为0时,容器被认为是空的。
从 item_container 表中获取特定项目的体积 > 0 的物理项目容器的计数,对于套件也是如此
我们希望为每个项目获得一个“保留计数”编号,以反映套件库存。
例如,假设我们有一个名为 A 的项目,其计数为 42。如果我们正在创建一个包含名为 A 的项目和一个相应的 itemkit_container 的 itemkit,我们希望“reserved”的计数为 1 , 对于项目 A。
项目的“主查询”如下所示:
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;
项目表中的数据:
item_containers 表中的数据:
itemkits 表中的数据:
itemkit_item 表中的数据:
以及itemkit_containers中的数据:
可以看出,itemkit 及其库存的唯一记录包含项目 ID = 1,3的项目
这个问题是要找出如何查询“免费”(或保留)物理项目的数量,即 item_containers 库存,在任何一个时间点。
上面的查询,返回这个结果:
我们想要一个额外的字段,指示每个项目的“保留”计数,反映项目和 itemkits 的实际库存状态。
对于上面的数据,这将是
A -> Reserved = 1
B -> Reserved = 0
C -> Reserved = 1
D -> Reserved = 0
创建和填充上述表格的 db fiddle 在这里: DB Fiddle
我们使用的是 mysql 8.0。
注意:下面的答案接近正确。但是,它不会将 item_containers(实际库存)与 itemkit_container 记录相关联,而是将 itemkit 记录相关联。通过将 itemkit_containers 表中的填充字段切换为“0”,这一点变得清晰。即:
即使不再填充套件,输出也会显示相同的“保留”计数。在这种情况下,保留应等于“0”。 这是该案例的小提琴:Fiddle where Reserved should be all '0'
【问题讨论】:
为所有表提供 CREATE TABLE,一些示例数据作为 INSERT INTO 和期望的结果。 @Akina 我用表格和数据更新了问题。我也简化了措辞。用于创建和填充表的 SQL 也是问题的一部分。 请说明您使用的是哪个版本的mysql。 我不明白“填充”列。如果项目 1 有三个容器,体积分别为 0、10、15,那么它有 25 件库存。如果物品 1 是套件 10、套件 20 和套件 30 的一部分,其中套件 10 和 20 被填充而套件 30 没有,那么物品 1 有多少件被保留,多少件是免费的? @Thorsten Kettner。好问题。任何体积 > 0 的物品容器都是“有效的”并且可以成为套件的一部分。体积 > 0 的项目容器被计为“1”,无论其体积如何。在 kit_containers 表中找到的填充标志表示套件已“填充”,并带有相关的物品容器。但是,填充套件不会显示使用了哪个物品容器,只会显示其类型。 【参考方案1】:首先计算总计并从适当的容器中保留,然后离开连接总计并保留给项目。
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
【讨论】:
这看起来比我给出的答案要短一些。【参考方案2】:感谢您提供如此详细的描述和所有必要的示例数据。
正如您已经在查询中尝试过的那样,您可以通过加入 items 和 item_containers 表来获得带有数量的项目。为了计算免费或保留项目,您需要离开连接 itemkit_containsers 表,因为套件中项目的库存存储在那里。因此,只需计算 itemkit_containers 中任何项目的数量,然后您就得到了您的保留数量,并通过从 item_containsers 表的 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
带有填充和未填充 itemkit_containsers 的 Db-Fiddle:
选择查询(示例数据):
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 |
db
【讨论】:
请查看这个答案,如果我遗漏了什么,请告诉我 我的回复延迟了。这是问题的答案,但是我认为我的问题和输入数据缺少重要的一点。我会更新这个问题并用一些更新的数据做一个小提琴。尽管如此,肯定会奖励赏金! 我将问题更新为更清晰。问题是与 itemkits 相关的 itemcontainers 的答案,而不是 itemkit_containers。我从问题中删除了“免费”的概念,因为“保留”是这里的主要问题。 @TotteKarlsson 感谢您的提琴。我已经改变了我的答案。请看一看。 @TotteKarlsson 我还创建了一个小提琴,我在其中创建了两个 itemkit,其中一个填充状态为 1,另一个在 itemkit_containsers 中填充状态为 0。请检查查询和输出。希望这是您正在寻找的。我认为没有必要这么多的左加入。如果我遗漏了什么,请告诉我。【参考方案3】:添加了考虑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
【讨论】:
以上是关于用于计算“保留”库存项目的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章
库存 SQL 查询出现歧义错误,其中两个字段应相等以进行计算。 MS-ACCESS