如何使用 mysql 子查询减去库存和销售额?

How to subtract inventory and sale using mysql subquery?

正在尝试了解有关子查询的更多信息。我正在寻找一种方法来减去和比较两个表。

  1. 库存
  2. 销售额

我的数据记录如下:

库存:

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 byorder 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

我不知道您将如何处理库存中的变化,或者需要从库存中扣除的销售额要回溯多久。这解决了您所写的问题。

样本数据非常有限,但我相信我们可以做出这些假设。

  1. 您不能出售物品,除非它已被记录为库存
  2. 您可以拥有尚未售出的库存物品(尚未)

如果不需要汇总库存,那么您可以使用单个 "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).

定义好这些键后,我们就可以知道设置完美的长性能子查询了。

  1. 第一个假设:您在清单中为 (store_id, product_id)
  2. 定义了一个主键

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;
  1. 第二个假设:只是一个索引,事情变得更复杂

除了不查询 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