如何使用 mysql 子查询减去库存和销售额?
How to subtract inventory and sale using mysql subquery?
正在尝试了解有关子查询的更多信息。我正在寻找一种方法来减去和比较两个表。
- 库存
- 销售额
我的数据记录如下:
库存:
mysql> select store_id, product_id, sum(quantity) as inventory from inventories where store_id = 1 group by product_id;
+----------+------------+-----------+
| store_id | product_id | inventory |
+----------+------------+-----------+
| 1 | 8 | 24 |
| 1 | 10 | 4 |
| 1 | 14 | 24 |
+----------+------------+-----------+
3 rows in set (0.00 sec)
销售额
mysql> select store_id, product_id, sum(quantity) as sales from sales where store_id = 1 group by product_id;
+----------+------------+-------+
| store_id | product_id | sales |
+----------+------------+-------+
| 1 | 8 | 12 |
| 1 | 14 | 2 |
| 1 | 8 | 1 |
+----------+------------+-------+
2 rows in set (0.00 sec)
获得以下结果的正确子查询是什么?
+----------+------------+-----------+-------+-----------+
| store_id | product_id | inventory | sales | remaining |
+----------+------------+-----------+-------+-----------+
| 1 | 8 | 24 | 12 | 12 |
| 1 | 14 | 24 | 2 | 22 |
| 1 | 8 | 12 | 1 | 11 |
+----------+------------+-----------+-------+-----------+
尝试将两个表连接为:
SELECT I.store_id, I.product_id, I.inventory, S.sales, (I.inventory - S.sales) AS remaining
FROM Sales S INNER JOIN INVENTOR I
ON I.store_id = S.store_id
AND I.product_id = S.product_id
要达到预期的产出,您需要计算 运行 产品销售总额。要获得有意义的数据,sales
table 中的数据必须按时间顺序排列。因此,您至少还需要一个字段来对数据进行排序——无论是时间戳还是 id
字段都没有关系。假设在 sales table 中有一个 id
字段。这是一个获取您所描述内容的查询:
SELECT
sales.id,
sales.store_id,
sales.product_id,
inventories.quantity-IFNULL(SUM(sales_2.quantity), 0) as inventory,
sales.quantity as sales,
inventories.quantity-IFNULL(SUM(sales_2.quantity), 0) - sales.quantity as remaining
FROM
sales
INNER JOIN
inventories ON inventories.store_id = sales.store_id
AND inventories.product_id = sales.product_id
LEFT JOIN
sales AS sales_2 ON sales_2.store_id = sales.store_id
AND sales_2.product_id = sales.product_id
AND sales_2.id < sales.id
GROUP BY sales.id , sales.store_id , sales.product_id
ORDER BY sales.id
sales
table 的第二个实例称为 sales_2
用于计算早期销售额的总和 (sales_2.id<sales.id
)
您可以从 select
子句中排除 sales.id
,但您需要将其保留在 group by
和 order by
.
中
您可以使用查询结果并将它们结合起来计算每个产品的剩余数量
SELECT
a.store_id,
a.product_id,
a.inventory,
b.sales,
a.inventory - b.sales AS remaining
FROM (
SELECT store_id, product_id, COALESCE(SUM(quantity),0) AS inventory
FROM inventories WHERE store_id = 1
GROUP BY product_id) a
LEFT JOIN (
SELECT store_id, product_id, COALESCE(SUM(quantity),0) AS sales
FROM sales WHERE store_id = 1
GROUP BY product_id ) b USING(store_id, product_id)
我发布了一个答案,然后重新阅读了您想要的内容。我意识到我误读了一些东西,我看到你确实希望将销售额视为按顺序扣除的单独交易,即 "history"。您仍然需要某种交易 ID 或交易日期来确定应用它们的顺序。另一个答案中也已经指出了这一点。也许您想依赖 MySQL 行 ID 或其他东西。我对 MySQL 的了解还不够多,无法告诉你这方面的帮助。
select
i.store_id, i.product_id,
i.inventory - s.previous_sales as inventory,
s.quantity as sales,
i.inventory - s.previous_sales - s.quantity as remaining
from
inventories as i inner join
(
select store_id, product_id, quantity,
(
select sum(quantity)
from sales as s2
where
s2.store_id = s.store_id and s2.product_id = s.product_id
/* all sales for this store and product prior to this one */
and s2.[sequencing column] < s.[sequencing column]
) as previous_sales
from sales
group by store_id, product_id
) as s
on s.store_id = i.store_id and s.product_id = i.product_id
where
i.store_id = 1
我不知道您将如何处理库存中的变化,或者需要从库存中扣除的销售额要回溯多久。这解决了您所写的问题。
样本数据非常有限,但我相信我们可以做出这些假设。
- 您不能出售物品,除非它已被记录为库存
- 您可以拥有尚未售出的库存物品(尚未)
如果不需要汇总库存,那么您可以使用单个 "derived table",这是一种子查询,如下所示:
SELECT
I.store_id
, I.product_id
, COALESCE(I.inventory, 0) AS INVENTORY
, COALESCE(S.sales, 0) AS SALES
, COALESCE(I.inventory, 0) - COALESCE(S.sales, 0) AS REMAINING
FROM Inventory I
LEFT JOIN (
SELECT
store_id
, product_id
, SUM(sales) AS SALES
FROM Sales
WHERE store_id = 1
GROUP BY
product_id
) S ON I.store_id = S.store_id
AND I.product_id = S.product_id
WHERE I.store_id = 1
ORDER BY
I.store_id
, I.product_id
;
如果还需要汇总库存,您可以像这样使用 2 "derived tables":
SELECT
I.store_id
, I.product_id
, COALESCE(I.inventory, 0) AS INVENTORY
, COALESCE(S.sales, 0) AS SALES
, COALESCE(I.inventory, 0) - COALESCE(S.sales, 0) AS REMAINING
FROM (
SELECT
store_id
, product_id
, SUM(inventory) AS INVENTORY
FROM Inventory
WHERE store_id = 1
GROUP BY
product_id
) I
LEFT JOIN (
SELECT
store_id
, product_id
, SUM(sales) AS SALES
FROM Sales
WHERE store_id = 1
GROUP BY
product_id
) S ON I.store_id = S.store_id
AND I.product_id = S.product_id
ORDER BY
I.store_id
, I.product_id
;
或者您可以在 select 子句中使用 "correlated subqueries",如下所示:
SELECT
I.store_id
, I.product_id
, COALESCE((
SELECT
SUM(sales)
FROM sales S
WHERE S.store_id = I.store_id
AND S.product_id = I.product_id
), 0) AS SALES
, I.inventory - COALESCE((
SELECT
SUM(sales)
FROM sales S
WHERE S.store_id = I.store_id
AND S.product_id = I.product_id
), 0) AS REMAINING
FROM Inventory AS I
WHERE I.store_id = 1
ORDER BY
I.store_id
, I.product_id
;
你问过哪个是"proper sub-query"。我相信以上所有内容在语法上都是正确的,但是 "proper" 我不完全理解。如果你的意思是哪个表现最好,我会建议派生表优先于相关子查询,但要得出适用于所有情况的答案几乎是不可能的。
要评估最佳性能,需要更好地定义数据、表和索引,然后强烈建议使用执行计划。也可能不使用任何子查询是最好的方法(即连接)。
在此处查看上面的查询作为演示:http://sqlfiddle.com/#!9/fa6b6/1
理想的子查询将是使用您的 table 键的子查询。
对于您的清单 table,您应该在 (store_id, product_id) 上有一个 PRIMARY KEY。
ALTER TABLE inventories ADD PRIMARY KEY (store_id, product_id).
如果这不是唯一的(在这种情况下,它实际上不是库存,而是 "incoming item" table),您可以为这两列设置索引。
ALTER TABLE inventories ADD INDEX (store_id, product_id).
您必须在销售中定义相同的索引table
ALTER TABLE sales ADD INDEX (store_id, product_id).
定义好这些键后,我们就可以知道设置完美的长性能子查询了。
- 第一个假设:您在清单中为 (store_id, product_id)
定义了一个主键
sql:
SELECT t1.store_id, t1.product_id, t1.quantity inventory,
IFNULL(sum(t2.quantity), 0) sales,
t1.quantity-IFNULL(sum(t2.quantity), 0) remaining
FROM inventories t1
LEFT JOIN sales t2 ON t1.store_id=t2.store_id
AND t1.product_id=t2.product_id
WHERE t1.store_id=1
GROUP BY t1.store_id, t1.product_id;
- 第二个假设:只是一个索引,事情变得更复杂
除了不查询 inventories
table,你做同样的事情,你将用以下子查询替换它:
SELECT store_id, product_id, SUM(quantity) quantity
FROM inventory
GROUP BY store_id, product_id;
结果如下:
SELECT t1.store_id, t1.product_id, t1.quantity inventory,
IFNULL(sum(t2.quantity), 0) sales,
t1.quantity-IFNULL(sum(t2.quantity), 0) remaining
FROM
(SELECT store_id, product_id, SUM(quantity) quantity
FROM inventory
GROUP BY store_id, product_id) t1
LEFT JOIN sales t2 ON t1.store_id=t2.store_id
AND t1.product_id=t2.product_id
WHERE t1.store_id=1
GROUP BY t1.store_id, t1.product_id;
假设您有 table 个产品和 table 个商店,然后将它们连接在一起,以获得产品和商店的组合。这样,一行就不会依赖于您感兴趣的商店中仍在库存中的产品,或者您感兴趣的商店中尚未售出的产品。
然后 LEFT OUTER JOIN 子查询获取库存和销售额或每个产品/商店。
SELECT b.store_id, a.product_id, IFNULL(c.inventory, 0), IFNULL(d.sales, 0) , (IFNULL(c.inventory, 0) - IFNULL(d.sales, 0)) AS remaining
FROM products a
INNER JOIN stores b
ON b.store_id = 1
LEFT OUTER JOIN
(
SELECT store_id, product_id, SUM(quantity) AS inventory
FROM inventories
WHERE store_id = 1
GROUP BY store_id, product_id
) c
ON a.product_id = c.product_id
AND b.store_id = c.store_id
LEFT OUTER JOIN
(
SELECT store_id, product_id, SUM(quantity) AS sales
FROM sales
WHERE store_id = 1
GROUP BY store_id, product_id
) d
ON a.product_id = d.product_id
AND b.store_id = d.store_id
正在尝试了解有关子查询的更多信息。我正在寻找一种方法来减去和比较两个表。
- 库存
- 销售额
我的数据记录如下:
库存:
mysql> select store_id, product_id, sum(quantity) as inventory from inventories where store_id = 1 group by product_id;
+----------+------------+-----------+
| store_id | product_id | inventory |
+----------+------------+-----------+
| 1 | 8 | 24 |
| 1 | 10 | 4 |
| 1 | 14 | 24 |
+----------+------------+-----------+
3 rows in set (0.00 sec)
销售额
mysql> select store_id, product_id, sum(quantity) as sales from sales where store_id = 1 group by product_id;
+----------+------------+-------+
| store_id | product_id | sales |
+----------+------------+-------+
| 1 | 8 | 12 |
| 1 | 14 | 2 |
| 1 | 8 | 1 |
+----------+------------+-------+
2 rows in set (0.00 sec)
获得以下结果的正确子查询是什么?
+----------+------------+-----------+-------+-----------+
| store_id | product_id | inventory | sales | remaining |
+----------+------------+-----------+-------+-----------+
| 1 | 8 | 24 | 12 | 12 |
| 1 | 14 | 24 | 2 | 22 |
| 1 | 8 | 12 | 1 | 11 |
+----------+------------+-----------+-------+-----------+
尝试将两个表连接为:
SELECT I.store_id, I.product_id, I.inventory, S.sales, (I.inventory - S.sales) AS remaining
FROM Sales S INNER JOIN INVENTOR I
ON I.store_id = S.store_id
AND I.product_id = S.product_id
要达到预期的产出,您需要计算 运行 产品销售总额。要获得有意义的数据,sales
table 中的数据必须按时间顺序排列。因此,您至少还需要一个字段来对数据进行排序——无论是时间戳还是 id
字段都没有关系。假设在 sales table 中有一个 id
字段。这是一个获取您所描述内容的查询:
SELECT
sales.id,
sales.store_id,
sales.product_id,
inventories.quantity-IFNULL(SUM(sales_2.quantity), 0) as inventory,
sales.quantity as sales,
inventories.quantity-IFNULL(SUM(sales_2.quantity), 0) - sales.quantity as remaining
FROM
sales
INNER JOIN
inventories ON inventories.store_id = sales.store_id
AND inventories.product_id = sales.product_id
LEFT JOIN
sales AS sales_2 ON sales_2.store_id = sales.store_id
AND sales_2.product_id = sales.product_id
AND sales_2.id < sales.id
GROUP BY sales.id , sales.store_id , sales.product_id
ORDER BY sales.id
sales
table 的第二个实例称为 sales_2
用于计算早期销售额的总和 (sales_2.id<sales.id
)
您可以从 select
子句中排除 sales.id
,但您需要将其保留在 group by
和 order by
.
您可以使用查询结果并将它们结合起来计算每个产品的剩余数量
SELECT
a.store_id,
a.product_id,
a.inventory,
b.sales,
a.inventory - b.sales AS remaining
FROM (
SELECT store_id, product_id, COALESCE(SUM(quantity),0) AS inventory
FROM inventories WHERE store_id = 1
GROUP BY product_id) a
LEFT JOIN (
SELECT store_id, product_id, COALESCE(SUM(quantity),0) AS sales
FROM sales WHERE store_id = 1
GROUP BY product_id ) b USING(store_id, product_id)
我发布了一个答案,然后重新阅读了您想要的内容。我意识到我误读了一些东西,我看到你确实希望将销售额视为按顺序扣除的单独交易,即 "history"。您仍然需要某种交易 ID 或交易日期来确定应用它们的顺序。另一个答案中也已经指出了这一点。也许您想依赖 MySQL 行 ID 或其他东西。我对 MySQL 的了解还不够多,无法告诉你这方面的帮助。
select
i.store_id, i.product_id,
i.inventory - s.previous_sales as inventory,
s.quantity as sales,
i.inventory - s.previous_sales - s.quantity as remaining
from
inventories as i inner join
(
select store_id, product_id, quantity,
(
select sum(quantity)
from sales as s2
where
s2.store_id = s.store_id and s2.product_id = s.product_id
/* all sales for this store and product prior to this one */
and s2.[sequencing column] < s.[sequencing column]
) as previous_sales
from sales
group by store_id, product_id
) as s
on s.store_id = i.store_id and s.product_id = i.product_id
where
i.store_id = 1
我不知道您将如何处理库存中的变化,或者需要从库存中扣除的销售额要回溯多久。这解决了您所写的问题。
样本数据非常有限,但我相信我们可以做出这些假设。
- 您不能出售物品,除非它已被记录为库存
- 您可以拥有尚未售出的库存物品(尚未)
如果不需要汇总库存,那么您可以使用单个 "derived table",这是一种子查询,如下所示:
SELECT
I.store_id
, I.product_id
, COALESCE(I.inventory, 0) AS INVENTORY
, COALESCE(S.sales, 0) AS SALES
, COALESCE(I.inventory, 0) - COALESCE(S.sales, 0) AS REMAINING
FROM Inventory I
LEFT JOIN (
SELECT
store_id
, product_id
, SUM(sales) AS SALES
FROM Sales
WHERE store_id = 1
GROUP BY
product_id
) S ON I.store_id = S.store_id
AND I.product_id = S.product_id
WHERE I.store_id = 1
ORDER BY
I.store_id
, I.product_id
;
如果还需要汇总库存,您可以像这样使用 2 "derived tables":
SELECT
I.store_id
, I.product_id
, COALESCE(I.inventory, 0) AS INVENTORY
, COALESCE(S.sales, 0) AS SALES
, COALESCE(I.inventory, 0) - COALESCE(S.sales, 0) AS REMAINING
FROM (
SELECT
store_id
, product_id
, SUM(inventory) AS INVENTORY
FROM Inventory
WHERE store_id = 1
GROUP BY
product_id
) I
LEFT JOIN (
SELECT
store_id
, product_id
, SUM(sales) AS SALES
FROM Sales
WHERE store_id = 1
GROUP BY
product_id
) S ON I.store_id = S.store_id
AND I.product_id = S.product_id
ORDER BY
I.store_id
, I.product_id
;
或者您可以在 select 子句中使用 "correlated subqueries",如下所示:
SELECT
I.store_id
, I.product_id
, COALESCE((
SELECT
SUM(sales)
FROM sales S
WHERE S.store_id = I.store_id
AND S.product_id = I.product_id
), 0) AS SALES
, I.inventory - COALESCE((
SELECT
SUM(sales)
FROM sales S
WHERE S.store_id = I.store_id
AND S.product_id = I.product_id
), 0) AS REMAINING
FROM Inventory AS I
WHERE I.store_id = 1
ORDER BY
I.store_id
, I.product_id
;
你问过哪个是"proper sub-query"。我相信以上所有内容在语法上都是正确的,但是 "proper" 我不完全理解。如果你的意思是哪个表现最好,我会建议派生表优先于相关子查询,但要得出适用于所有情况的答案几乎是不可能的。
要评估最佳性能,需要更好地定义数据、表和索引,然后强烈建议使用执行计划。也可能不使用任何子查询是最好的方法(即连接)。
在此处查看上面的查询作为演示:http://sqlfiddle.com/#!9/fa6b6/1
理想的子查询将是使用您的 table 键的子查询。
对于您的清单 table,您应该在 (store_id, product_id) 上有一个 PRIMARY KEY。
ALTER TABLE inventories ADD PRIMARY KEY (store_id, product_id).
如果这不是唯一的(在这种情况下,它实际上不是库存,而是 "incoming item" table),您可以为这两列设置索引。
ALTER TABLE inventories ADD INDEX (store_id, product_id).
您必须在销售中定义相同的索引table
ALTER TABLE sales ADD INDEX (store_id, product_id).
定义好这些键后,我们就可以知道设置完美的长性能子查询了。
- 第一个假设:您在清单中为 (store_id, product_id) 定义了一个主键
sql:
SELECT t1.store_id, t1.product_id, t1.quantity inventory,
IFNULL(sum(t2.quantity), 0) sales,
t1.quantity-IFNULL(sum(t2.quantity), 0) remaining
FROM inventories t1
LEFT JOIN sales t2 ON t1.store_id=t2.store_id
AND t1.product_id=t2.product_id
WHERE t1.store_id=1
GROUP BY t1.store_id, t1.product_id;
- 第二个假设:只是一个索引,事情变得更复杂
除了不查询 inventories
table,你做同样的事情,你将用以下子查询替换它:
SELECT store_id, product_id, SUM(quantity) quantity
FROM inventory
GROUP BY store_id, product_id;
结果如下:
SELECT t1.store_id, t1.product_id, t1.quantity inventory,
IFNULL(sum(t2.quantity), 0) sales,
t1.quantity-IFNULL(sum(t2.quantity), 0) remaining
FROM
(SELECT store_id, product_id, SUM(quantity) quantity
FROM inventory
GROUP BY store_id, product_id) t1
LEFT JOIN sales t2 ON t1.store_id=t2.store_id
AND t1.product_id=t2.product_id
WHERE t1.store_id=1
GROUP BY t1.store_id, t1.product_id;
假设您有 table 个产品和 table 个商店,然后将它们连接在一起,以获得产品和商店的组合。这样,一行就不会依赖于您感兴趣的商店中仍在库存中的产品,或者您感兴趣的商店中尚未售出的产品。
然后 LEFT OUTER JOIN 子查询获取库存和销售额或每个产品/商店。
SELECT b.store_id, a.product_id, IFNULL(c.inventory, 0), IFNULL(d.sales, 0) , (IFNULL(c.inventory, 0) - IFNULL(d.sales, 0)) AS remaining
FROM products a
INNER JOIN stores b
ON b.store_id = 1
LEFT OUTER JOIN
(
SELECT store_id, product_id, SUM(quantity) AS inventory
FROM inventories
WHERE store_id = 1
GROUP BY store_id, product_id
) c
ON a.product_id = c.product_id
AND b.store_id = c.store_id
LEFT OUTER JOIN
(
SELECT store_id, product_id, SUM(quantity) AS sales
FROM sales
WHERE store_id = 1
GROUP BY store_id, product_id
) d
ON a.product_id = d.product_id
AND b.store_id = d.store_id