我想从两个表的数据中创建一个新表

Posted

技术标签:

【中文标题】我想从两个表的数据中创建一个新表【英文标题】:I wanna create a new table from the data of two tables 【发布时间】:2021-09-26 03:19:33 【问题描述】:

我有一个A表来存储商品的商品名称、库存商品的数量和输入天数。在不利条件下,上述产品的发票将稍后发送。我将产品名称和发票数量放在 B 表中。这里的问题是我想检查有发票和没有发票的货物数量。使用发票更新货物将遵循 FIFO。

例子:

表 A

id good_id num created_at
1 1 10 2021-09-24
2 1 5 2021-09-25

表 B

id good_id num_invoice
1 1 12

我通过创建一个与 A 表具有相同数据的新 C 表来解决它

表 C

id good_id current_number created_at invoice_number
1 1 10 2021-09-24 null
2 1 5 2021-09-25 null

然后我通过good_id获取表B组中的数据并将其存储在$data中。 使用php foreach $data 并检查条件:

我更新了 C 表 ORDER BY created_at DESC 限制 1 如下:

如果 (tableC.current_num - $data['num'] current_number = 0, invoice_number = $data['num'] - tableC.current_num。更新值$data['num'] = $data['num'] - tableC.current_num

如果 (tableC.current_num - $data['num'] > 0) 或 (tableC.current_num - $data['num'] = 0) 然后更新 current_number = tableC.@ 987654345@ - $data['num'], invoice_number = $data['num'].

更新后的表 C

id good_id current_number created_at invoice_number
1 1 0 2021-09-24 10
2 1 3 2021-09-25 2

我用php 解决了这个问题。但是,对于大约 100,000 行的数据集,我认为后端处理将需要很长时间。有人可以给我一个更聪明的方法来处理这个吗?

【问题讨论】:

您使用的是什么数据库和版本(MariaDB 10.5、mysql 8.x、SQL Server 2019、PostgreSQL 16 等)? 看起来你可以加入这 2 个表没有? 我使用的是 MySQL 5.7 @nhhthong 查看更新的答案,其中也包含 MySQL 5.7+ 的解决方案 【参考方案1】:

MySQL 5.7 的更新解决方案:

支持 MySQL 5.7+、PG、MariaDB 10.2.2 之前的测试用例等:

Test case for MySQL 5.7+, etc

对于 MySQL 5.7: 这将替换窗口函数(用于运行 SUM)并使用派生表而不是 WITH clause

SELECT id, good_id
     , num - GREATEST(num - GREATEST(balance, 0), 0) AS num
     , created_at
     , GREATEST(num - GREATEST(balance, 0), 0) AS invoice_num
  FROM (
         SELECT MIN(t2.id) AS id, MIN(t2.num) AS num
              , t2.good_id, t2.created_at
              , MIN(o.num_invoice) AS num_invoice
              , SUM(t1.num) - MIN(o.num_invoice) AS balance
           FROM tableA AS t1
           JOIN tableA AS t2
             ON t1.good_id = t2.good_id
            AND t1.created_at <= t2.created_at
           JOIN (
                   SELECT good_id, SUM(num_invoice) AS num_invoice
                     FROM tableB
                    GROUP BY good_id
                ) AS o
             ON o.good_id = t1.good_id
          GROUP BY t2.good_id, t2.created_at
       ) AS cte2
 ORDER BY created_at
;

对于在GROUP BY 中正确处理功能依赖的数据库,我们可以只使用GROUP BY t2.idtableAprimary key)并删除MIN(t2.id)MIN(t2.num)

像这样:

Test case

-- For MySQL 5.7
SELECT id, good_id
     , num - GREATEST(num - GREATEST(balance, 0), 0) AS num
     , created_at
     , GREATEST(num - GREATEST(balance, 0), 0) AS invoice_num
  FROM (
         SELECT t2.id, t2.num
              , t2.good_id, t2.created_at
              , MIN(o.num_invoice) AS num_invoice
              , SUM(t1.num) - MIN(o.num_invoice) AS balance
           FROM tableA AS t1
           JOIN tableA AS t2
             ON t1.good_id = t2.good_id
            AND t1.created_at <= t2.created_at
           JOIN (
                   SELECT good_id, SUM(num_invoice) AS num_invoice
                     FROM tableB
                    GROUP BY good_id
                ) AS o
             ON o.good_id = t1.good_id
          GROUP BY t2.id
       ) AS cte2
 ORDER BY created_at
;

使用窗口函数和WITH clause的原始答案:

这是一个使用 PG 13 的测试用例,但适用于 MySQL 8 或 MariaDB 10.2.2+。

注意:我将其保留为仅生成所请求详细信息的查询。目前尚不清楚第三张桌子是否必要。如果需要,这可用于更新(或创建)该表。

测试用例:

Working test case

CTE 术语:

    cte1 - 计算总请求货物 cte2 - 根据日期运行库存计算运行余额

最后,我们使用 cte2 来确定问题所要求的分配和剩余的货物。

WITH cte1 (good_id, num_invoice) AS (
        SELECT good_id, SUM(num_invoice) AS num_invoice
          FROM tableB
         GROUP BY good_id
     )
   , cte2 AS (
       SELECT a.*, o.num_invoice
            , SUM(num) OVER (PARTITION BY a.good_id ORDER BY created_at) - o.num_invoice AS balance
         FROM tableA AS a
         JOIN cte1   AS o
           ON o.good_id = a.good_id
     )
SELECT id, good_id
     , num - GREATEST(num - GREATEST(balance, 0), 0) AS num
     , created_at
     , GREATEST(num - GREATEST(balance, 0), 0) AS invoice_num
  FROM cte2
 ORDER BY created_at
;

结果:

+----+---------+------+------------+-------------+
| id | good_id | num  | created_at | invoice_num |
+----+---------+------+------------+-------------+
|  1 |       1 |    0 | 2021-09-24 |          10 |
|  2 |       1 |    3 | 2021-09-25 |           2 |
|  3 |       1 |    7 | 2021-09-26 |           0 |
+----+---------+------+------------+-------------+

注意:我为 7 种商品 (id = 3) 添加了一个额外的现有条目来测试边缘情况。

测试用例的设置:

CREATE TABLE tableA (
   id           int primary key
 , good_id      int
 , num          int
 , created_at   date
);

CREATE TABLE tableB (
   id           int primary key
 , good_id      int
 , num_invoice  int
);

INSERT INTO tableA VALUES
  (1,1,10,'2021-09-24')
, (2,1, 5,'2021-09-25')
, (3,1, 7,'2021-09-26')
;

INSERT INTO tableB VALUES
  (1,1,12)
;

【讨论】:

非常感谢。这是个好主意。但是,我使用的系统是 MYSQL 5.7。不过这也是我以后升级MYSQL时的好主意。 @nhhthong 这也可以在 MySQL 5.7 中完成……一会儿。

以上是关于我想从两个表的数据中创建一个新表的主要内容,如果未能解决你的问题,请参考以下文章

在 symfony2 中创建表

如何在 Google Bigquery 中创建动态更改数据集的查询?

为用户授予在 postgresql 中创建的任何新表的权限

在数据库中创建一个新表 [关闭]

如何在现有 Sqlite DB 中添加新表

我想要两个在一个按钮中创建两个功能