使用自身的值更新表,但选择不同表中的条件

问题描述 投票:1回答:1

我有一个只包含外键的联结表,如下所示:

 JUNCTION
+-------+-----------+---------+---------+
|  id   | module_id | comp_id | ins_id  |
+-------+-----------+---------+---------+
| 1     | 1         | 2       | 8       |
| 2     | 2         | 4       | 9       |
| 3     | 3         | 4       | 10      |
| 4     | 4         | 1       | 10      |
| 5     | 3         | 5       | 11      |
| 6     | 4         | 1       | 11      |
| 7     | 5         | 42      | 11
+-------+-----------+---------+---------+

其中一个引用的表是modules,如下所示:

 MODULES
+-----------+---------+---------+
| id        | name    | version |
+-----------+---------+---------+
| 1         | default | 1       |
| 2         | bar     | 1       |
| 3         | foo     | 1       |
| 4         | foo     | 3       |
| 5         | foo     | 2       |
+-----------+---------+---------+

有没有办法在SQL中更新此表,以便联结表中的联结ID comp_id4更新为comp_id 4(与模块3同名,但更高版本),并且联结ID 6获取comp_id 42,其中是前面的模块版本(2)的comp_id,因为它们具有匹配的模块名称和ins_id,依此类推所有具有相同标准的记录?

在散文中:更新联结表中具有comp_id 1的所有模块引用,使用具有匹配模块名称的模块的comp_id更新它们,将其设置为具有下一个最小版本的模块的comp_id,并匹配ins_id(和can被联结表中的其他列约束)

或者,在业务逻辑中,这会以编程方式更好地处理吗?

postgresql postgresql-9.5
1个回答
0
投票

我相信我已经解决了它:答案是window functions(虽然连接的一些组合可能能够做到。我越来越接近,但认为这将更优雅和可理解)

首先,我必须创建一个CTE,将模块名称和版本映射到联结表中的值:

WITH module_mapping AS (SELECT j.module_id, j.ins_id, j.comp_id, m.name, m.version
   FROM junction AS j
   JOIN modules AS m ON m.id = j.module_id
)

使用该CTE,我可以创建一个窗口函数,用于选择具有相同模块的记录:

, win AS (SELECT name, version, module_id, ins_id, comp_id, first_value(comp_id) 
   OVER (PARTITION BY name, ins_id ORDER BY version DESC
     ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) 
   FROM module_mapping)

因此,当我进行以下查询时,我可以看到first_value列具有我正在寻找的记录:

WITH module_mapping AS (SELECT j.module_id, j.ins_id, j.comp_id, m.name, m.version
       FROM junction AS j
       JOIN modules AS m ON m.id = j.module_id
    )
, win AS (SELECT name, version, module_id, ins_id, comp_id, first_value(comp_id) 
OVER (PARTITION BY name, ins_id ORDER BY version DESC
     ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) -- this key part excludes the current row, since it will have the largest (newest) module version
   FROM module_mapping)
SELECT * FROM win WHERE comp_id = 1;

+-------+---------+-----------+--------+---------+-------------+
| name  | version | module_id | ins_id | comp_id | first_value |
+-------+---------+-----------+--------+---------+-------------+
| "foo" | 3       | 4         | 10     | 1       | 4           |
| "foo" | 3       | 4         | 11     | 1       | 42          |
+-------+---------+-----------+--------+---------+-------------+

所以有了这两个CTE,我可以调用我的更新sql:

UPDATE junction AS j
SET comp_id = win.new_comp
FROM win
WHERE j.comp_id = 1 -- only change the default ones
AND j.module_id = win.module_id
AND j.ins_id = win.ins_id;
© www.soinside.com 2019 - 2024. All rights reserved.