我想从两个表的数据中创建一个新表
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.id
(tableA
的primary 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 中完成……一会儿。以上是关于我想从两个表的数据中创建一个新表的主要内容,如果未能解决你的问题,请参考以下文章