使用来自自身的值更新 table,但根据来自不同 table 的条件进行选择

Update a table with values from itself, but selecting on criteria from a different table

我有一个仅由外键组成的联结 table,如下所示:

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

引用的 table 之一是 modules,如下所示:

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

SQL 中是否有更新此 table 的方法,以便更新结点 table 中结点 ID 4comp_idcomp_id 4(与模块 3 同名,但版本更高),结点 ID 6 得到 comp_id 42,这是 comp_id前面的模块版本 (2),因为它们具有匹配的模块 namesins_id,对于具有相同的所有记录,依此类推标准?

在散文中: 更新结点 table 中具有 comp_id 1 的所有模块引用,使用具有匹配模块名称的模块的 comp_id 更新它们,将其设置为 comp_id具有下一个最小版本的模块,并匹配 ins_id(并且可以受到连接 table 中其他列的约束)

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

我相信我已经解决了:答案是 window functions(尽管某些连接组合可能能够做到这一点。我已经接近了,但认为这会更优雅和更容易理解)

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

中的值
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,我可以创建一个 window 函数,该函数将 select 具有相同模块的记录 :

, 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;