我有一个只包含外键的联结表,如下所示:
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_id
的4
更新为comp_id 4
(与模块3同名,但更高版本),并且联结ID 6
获取comp_id 42
,其中是前面的模块版本(2)的comp_id
,因为它们具有匹配的模块名称和ins_id,依此类推所有具有相同标准的记录?
在散文中:更新联结表中具有comp_id 1的所有模块引用,使用具有匹配模块名称的模块的comp_id更新它们,将其设置为具有下一个最小版本的模块的comp_id,并匹配ins_id
(和can被联结表中的其他列约束)
或者,在业务逻辑中,这会以编程方式更好地处理吗?
我相信我已经解决了它:答案是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;